问题描述

给定一个排序的无限整数数组,我们需要找到一个给定目标数字的索引。数组是“无限”的,这意味着我们不能预先确定其大小,因此不能直接应用传统的二分查找。


方法概述

  1. 从小范围开始:最初假设目标元素位于一个小范围内(例如,索引 0 和 1 之间)。
  2. 动态扩展范围:如果在初始范围内未找到目标元素,则将范围的大小加倍进行进一步搜索。这种指数增长使我们能够迅速缩小目标可能所在的范围。
  3. 在范围内进行二分查找:一旦确定了包含目标的适当范围,就应用二分查找来高效地找到目标的索引。

代码

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. 二分查找

一旦我们知道目标值必须位于 startend 之间,就进行标准的二分查找。二分查找是对排序数组进行搜索的高效方法,因为它每步将搜索空间减半。关键比较是:

  • 如果目标值小于中间元素 (arr[mid]),则在左半边搜索。
  • 如果目标值大于中间元素,则在右半边搜索。
  • 如果目标值等于中间元素,则返回其索引。
4. 边界情况
  • 如果目标值小于数组中的最小元素,或者数组中不包含目标值,算法将返回 -1

时间复杂度

  1. 范围扩展:范围每次迭代都加倍,因此找到目标所在范围的操作次数为 O(log N)
  2. 二分查找:一旦找到范围,二分查找在 O(log M) 时间内完成,其中 M 是范围的大小。

因此,整体时间复杂度约为 O(log N + log M)