返回

巧用二分在“暗度陈仓”的数组中找数

人工智能

在编程世界的浩瀚海洋中,LeetCode是一座耸立的灯塔,指引着无数程序员的进阶之路。LeetCode 33 题,要求你在一个看似有序实则“暗度陈仓”的数组中使用二分法查找元素,可谓一个巧妙的挑战。

想象一个被恶作剧的数组,它原本有序排列,却在某处被一分为二,然后调皮地交换了顺序。此时,原本的二分法将不再奏效。但别担心,聪明的程序员们总会找到解决办法!

二分法捉迷藏

二分法,顾名思义,是一种通过不断将问题空间对半分,快速收敛至答案的搜索算法。在有序数组中,二分法可以将查找时间复杂度从 O(n) 优化到 O(log n)。

然而,面对“暗度陈仓”的数组,二分法捉迷藏就变得困难了。常规的二分法无法区分数组是从哪个位置开始交换的,可能陷入死循环。

曲线救国:寻找拐点

为了解决这个问题,我们需要找到数组中那个“暗度陈仓”的拐点,即交换顺序的位置。一旦找到拐点,问题就又可以回归到常规二分法了。

步骤详解:

  1. 判断数组是否存在拐点: 遍历数组,当发现数组元素不再单调递增时,就找到了拐点。
  2. 处理拐点前后的两个有序子数组: 将数组分为拐点前和拐点后的两个有序子数组,分别用二分法查找。
  3. 返回结果: 如果在任一子数组中找到目标元素,则返回其下标;否则返回 -1。

代码示例:

public int search(int[] nums, int target) {
    int left = 0, right = nums.length - 1;

    // 查找拐点
    while (left < right) {
        int mid = left + (right - left) / 2;
        if (nums[mid] < nums[left]) {
            right = mid;
        } else {
            left = mid + 1;
        }
    }

    // 拐点为 left
    int pivot = left;

    // 在拐点前后的两个子数组中分别二分查找
    if (target >= nums[pivot] && target <= nums[right]) {
        return binarySearch(nums, pivot, right, target);
    } else {
        return binarySearch(nums, 0, pivot - 1, target);
    }
}

private int binarySearch(int[] nums, int start, int end, int target) {
    while (start <= end) {
        int mid = start + (end - start) / 2;
        if (nums[mid] == target) {
            return mid;
        } else if (nums[mid] < target) {
            start = mid + 1;
        } else {
            end = mid - 1;
        }
    }
    return -1;
}

总结:

LeetCode 33 题巧妙地融合了二分法和拐点寻找,考验了程序员对算法的灵活运用和问题分解能力。通过找到拐点,我们将“暗度陈仓”的数组拆解为两个有序子数组,从而顺利地应用二分法进行查找。

面对编程挑战,跳出固有思维,巧用算法,往往能收获意想不到的收获。就像这道题中,二分法和拐点寻找的组合,让我们在“暗度陈仓”的数组中轻松寻宝!