二分查找,什么时候用right=mid-1,什么时候用right=mid?

Binary search, when to use right = mid - 1, and when to use right = mid?

我在 leetcode 上解决这个问题 https://leetcode.com/problems/leftmost-column-with-at-least-a-one/ 但我想不出一个直观的答案。

为什么右(或高)指针有时设置为 mid - 1,为什么有时设置为 mid 是正确的?

我知道我们必须始终设置 left = mid + 1 因为整数除法。当只剩下两个元素时,我们需要设置mid + 1以避免死循环。

但是在什么情况下使用 right = mid - 1,vs right = mid?

谢谢。

假设您正在对如下序列进行二进制搜索

.....0, 0, 0, 0, 1, 1, 1, 1, ....

你的决策函数 fn returns true 如果 1 的值保持 true

现在考虑您的目标是找到 0 的最后位置。在二分查找的每一步中,我们都会减少搜索space,这样我们就可以确定位置在范围内。 如果 fn returns true for mid 你知道 0 的最后位置将小于 mid (因为你想要最后一次出现 0 必须在第一次出现 1 之前)。因此,您将更新 right=mid-1。如果fn return false left=mid.

现在考虑您的目标是找到 1 的第一次出现。 现在如果 fn returns true 你将更新 right=mid 因为你知道 1 的第一次出现将在这个位置或它的左边。在这种情况下,如果 fn returns false,您将需要更新 left=mid+1.

小型数组的线性搜索优于二分搜索。因此,使用或不使用 -1 并不重要,因为您的搜索应该在 right-left < N 时中断,然后您可以在两者之间进行线性搜索(N 是一个参数,您可以通过 运行 基准找到适合您的特定应用程序。

我上次测量时,整数的 Iirc N 约为 700。

实现二分查找的模板有3个
模板 1:不依赖于其他元素和相邻元素。

while(left <= right){
    int mid = (int)Math.floor((left + right)/2);
    if(nums[mid] == target) return mid;
    else if(nums[mid] < target) {
        left = mid + 1;
    } else {
        right = mid - 1;
    }
}

模板 2:当搜索值需要访问当前索引及其右邻的索引时使用

while(left < right){
   // Prevent (left + right) overflow
   int mid = left + (right - left) / 2;
   if(nums[mid] == target){ return mid; }
   else if(nums[mid] < target) {
      left = mid + 1; }
   else {
      right = mid; }
}

模板 3:当搜索值需要访问当前索引及其左右相邻索引时使用

 while (left + 1 < right){
    // Prevent (left + right) overflow
    int mid = left + (right - left) / 2;
    if (nums[mid] == target) {
        return mid;
    } else if (nums[mid] < target) {
        left = mid;
    } else {
        right = mid;
    }
}

来源:LeetCode Explore