153(补充)关于二分查找问题循环条件的总结

48 阅读4分钟

先说结论:

1.当需要对left==right时,对应的元素进行校验的时候,如查找这个元素或者判断是否符合某个条件时,循环条件是left<=right 2.当不需要对left==right对应的元素进行校验时,如寻找最小值,或者为了防止死循环等,循环条件是left<right

1. left < right 的使用条件和环境

left < right 通常用于当你希望严格缩小区间,并且你的目标是最终让 left 和 right 相遇时确保区间内至少有两个元素(或更多)。这种方式保证了每次迭代都能把搜索区间减半,而避免了在单一元素区间时进行不必要的操作。

适用场景:

  • 旋转排序数组的最小值
    这类问题需要逐步收缩区间直到 left == right,而每次可能都保留 mid 作为候选答案,right = mid 或 left = mid + 1 都是允许的。
  • 查找某个元素
    在查找某个元素时,left < right 确保在每次缩小搜索区间时,mid 仍然是有效的查找位置,最后的答案一定在 left == right 时获得。

为什么是 left < right

  • 避免死循环:如果 left == right 时,right 不会再变动,可能导致死循环。例如,在旋转数组问题中,如果条件是 left <= right,当 left == right 时会继续进行,导致 right = mid,但区间并未收缩,可能陷入死循环。
  • 保证严格缩小区间:每次收缩时,left 和 right 会“严格”向中间靠拢,最终会精确收敛到 left == right

示例:

旋转排序数组最小值

while (left < right) {
    int mid = left + (right - left) / 2;
    if (nums[mid] <= nums[right]) {
        right = mid; // mid 可能是最小值,所以不排除
    } else {
        left = mid + 1; // mid 不是最小值,排除 mid
    }
}
return nums[left]; // left == right 时,返回最小值
  • 关键点left < right 保证了在每次迭代后区间会严格收缩,最后 left == right 时返回结果。

2. left <= right 的使用条件和环境

left <= right 通常用于当你希望包含所有区间元素,并且你需要通过每次对比来更新解的情况。这种方式适合那些需要对当前区间的所有元素进行检查并返回答案的场景,尤其是当你可能想检查 mid 本身是否是答案时。

适用场景:

  • 查找某个元素:当查找一个具体元素时,通常会使用 left <= right,因为你可能在区间的最后一次迭代时发现目标元素正好是 left 或 right,此时必须确保 left == right 仍然有效。
  • 搜索问题(如寻找区间内某个范围的最小值或最大值):比如说查找一个在有序区间内首次出现的元素,left <= right 可以让你确保最后不会跳过任何可能的解。

为什么是 left <= right

  • 可以检查 mid 本身:在 left <= right 的条件下,mid 可能会是你想要找的元素或最小值,因此每次都要对 mid 进行检查,并且允许 mid 成为结果。
  • 边界元素可能是答案:如果你希望检查所有的可能性,或者最小值出现在数组的边缘,left <= right 允许你最终检查这两个边界元素。

示例:

查找目标元素

while (left <= right) {
    int mid = left + (right - left) / 2;
    if (nums[mid] == target) {
        return mid;
    } else if (nums[mid] < target) {
        left = mid + 1;
    } else {
        right = mid - 1;
    }
}
return -1; // 目标不在数组中
  • 关键点:在每次迭代后,mid 都会被检查,left <= right 允许你直接使用 mid 作为一个有效的位置检查。

3. 选择 left < right 还是 left <= right

  • left < right

    • 通常用于 收敛查找区间的题目,最终 left == right 时会得到目标。
    • 用于 旋转排序数组查找最小/最大值等类型的题目。
    • 保证了每次迭代区间都缩小,避免死循环。
  • left <= right

    • 用于需要 检查每个位置,比如 查找目标元素查找某个条件成立的位置
    • 适合题目中需要 返回 mid 或检查 mid 是否为目标元素的情况。

4. 具体区别与对比

步骤一:搜索区间

  • left < right 确保不会被 mid 排除在搜索区间之外,避免重复搜索某些部分。
  • left <= right 允许在最后一次迭代时直接检查 mid,并且在某些情况下不需要额外的处理。

步骤二:区间收缩

  • left < right 每次收缩区间时,left 或 right 都会“严格”向中间靠拢,保证最终会收敛。
  • left <= right 可能在某些边界情况(如 left == right)不会改变区间,从而导致不必要的多次迭代。

5. 总结

  • left < right 用于 严格收缩区间,确保每次迭代有效收缩,避免死循环。它适用于 旋转数组最小值区间最小值查找等问题。
  • left <= right 用于查找元素,确保能检查整个区间,包括边界元素,适合于 查找某个元素 或 搜索首/尾元素 的问题。

希望这些总结能帮助你更清楚地理解两者之间的区别和使用场景!😊