问题描述
给定一个排序的无限整数数组,我们需要找到一个给定目标数字的索引。数组是“无限”的,这意味着我们不能预先确定其大小,因此不能直接应用传统的二分查找。
方法概述
- 从小范围开始:最初假设目标元素位于一个小范围内(例如,索引 0 和 1 之间)。
- 动态扩展范围:如果在初始范围内未找到目标元素,则将范围的大小加倍进行进一步搜索。这种指数增长使我们能够迅速缩小目标可能所在的范围。
- 在范围内进行二分查找:一旦确定了包含目标的适当范围,就应用二分查找来高效地找到目标的索引。
代码
java复制代码public class InfiniteArraySearch {
public static void main(String[] args) {
// 定义数组(为了演示,视作无限)
int[] arr = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15};
int target = 6;
// 调用函数以在无限数组中查找目标元素
int result = findElementInInfiniteArray(arr, target);
System.out.println("元素在索引处找到: " + result);
}
// 在无限数组中查找目标元素的函数
static int findElementInInfiniteArray(int[] arr, int target) {
// 从小范围开始
int start = 0;
int end = 1;
// 动态扩展范围,直到目标在范围内
while (target > arr[end]) {
int newStart = end + 1; // 更新起始索引为当前结束索引的下一位置
end = end + (end - start + 1) * 2; // 扩大范围
start = newStart; // 将起始索引移动到 newStart
}
// 在确定的范围内执行二分查找
return binarySearch(arr, target, start, end);
}
// 标准的二分查找实现
static int binarySearch(int[] arr, int target, int start, int end) {
while (start <= end) {
int mid = start + (end - start) / 2;
if (target < arr[mid]) {
end = mid - 1; // 将结束索引移动到左边
} else if (target > arr[mid]) {
start = mid + 1; // 将起始索引移动到右边
} else {
return mid; // 找到元素
}
}
return -1; // 元素未找到
}
}
代码解释
1. 主函数
主函数定义了一个示例数组 arr
和一个目标值 6
。为了简化,我们假设数组在这里是有限的,但在概念上,我们视它为无限。主函数然后调用 findElementInInfiniteArray
函数来搜索目标,如果找到目标,则打印其索引。
2. 范围扩展(线性扩展搜索区域)
在 findElementInInfiniteArray
方法中:
- 我们从假设元素位于范围 [0, 1] 开始。
- 如果目标值大于
arr[end]
处的值,则意味着目标不在当前范围内。因此,我们通过加倍范围来扩展范围(end = end + (end - start + 1) * 2
)。这有效地使我们每次迭代都能覆盖更多区域。
3. 二分查找
一旦我们知道目标值必须位于 start
和 end
之间,就进行标准的二分查找。二分查找是对排序数组进行搜索的高效方法,因为它每步将搜索空间减半。关键比较是:
- 如果目标值小于中间元素 (
arr[mid]
),则在左半边搜索。 - 如果目标值大于中间元素,则在右半边搜索。
- 如果目标值等于中间元素,则返回其索引。
4. 边界情况
- 如果目标值小于数组中的最小元素,或者数组中不包含目标值,算法将返回
-1
。
时间复杂度
- 范围扩展:范围每次迭代都加倍,因此找到目标所在范围的操作次数为
O(log N)
。 - 二分查找:一旦找到范围,二分查找在
O(log M)
时间内完成,其中M
是范围的大小。
因此,整体时间复杂度约为 O(log N + log M)
。