二分

704. 二分查找 - 啤酒加点醋 - 博客园 (cnblogs.com)中,介绍了起始点left和right的选择与while()中的关系,依照目前做题看来,这个依然是对的。但在做题中,仍然会产生怀疑,以至于需要一遍遍的在本子上画示意图。目前,总结一下关于left和right的变化方法:

1. 数组中存在重复数字,需要找出某个数在数组中第一次出现的位置和最后一次出现的位置。这个时候单纯的当nums[mid]==target时就不能得出答案了。

解决 方法如下:

  1. 第一次出现的位置:想象有一个标尺代表mid的位置,当nums[mid] == targert时,虽然不能确定mid是否为第一次出现的位置,但是我们需要让mid这个标尺往左移,怎么移动? right--

  2. 最后一次出现的位置:同上,nums[mid] == target时,让left ++ ,直到将标尺逼到最后一次出现的位置上。

2. mid 计算方法与left 和right 的关系

  通常来说我们都是向左逼近,原因是因为left和right的初始值设置和习惯。但是有一些题目需要我们向左逼近,比如69. x 的平方根 - 力扣(LeetCode) (leetcode-cn.com)。总结来说,有以下两个规律:

  1. 若right需要有类似right = mid的操作,而left为left = mid + 1的操作,让mid = left + (right - left) / 2;

  2. 若反过来,让mid = left + (right - left + 1) / 2;

   这是考虑到当left到right之间的数字为偶数时,在某些情况下不能跳出循环的现象

    以right = mid - 1; left = mid ; 且mid = left + (right - left) / 2为例子。

  如果whil(left <= right),且left和right指向同一个位置,则mid = right;而又有left=mid,无法跳出循环。

  如果while(left < right), 且left + 1 == right ,则mid = right;而又有left = mid,无法跳出循环。

3. 关于返回值问题。

  1. 查到某个数返回坐标类问题。如果查到了,那就在while循环内返回,

  2. 插入某个不存在的数,求插入坐标问题。

  以35. 搜索插入位置 - 力扣(LeetCode) (leetcode-cn.com为例子:

class Solution {
    public int searchInsert(int[] nums, int target) {
        int left = 0;
        int right = nums.length - 1;
        while(left <= right){
            int mid = left +  (right - left) / 2;
            if(nums[mid] < target){
                left = mid + 1;
            }else if (nums[mid] > target){
                right = mid - 1;
            }else{
                return mid;
            }
        }
        return left;
    }
}

因为while(left <= right),那么跳出循环一定是因为left == right。根据if.else语句,判定跳出循环前有left==right。

跳出有两方面原因,第一是left靠向right,是因为现在的left/right位置上的数小于target,那所以left = mid +  1 = left+1,就返回left咯;

第二是right 靠向left,是因为现在的left/right位置上的数大于target,所以呢,right = mid - 1 = left - 1 = right - 1,那就应该在现有位置上插入,故返回left。

 

如果代码是

class Solution {
    public int searchInsert(int[] nums, int target) {
        int left = 0;
        int right = nums.length;
        while(left < right){
            int mid = left +  (right - left) / 2;
            if(nums[mid] < target){
                left = mid + 1;
            }else if (nums[mid] > target){
                right = mid;
            }else{
                return mid;
            }
        }
        return right;
    }
}

跳出循环前一定满足left+1==right, 那跳出原因肯定是left = mid + 1 = left + 1的操作,而执行这种操作的原因是因为left处的值小于target,所以呀,应该将target插入left后,即left+1,也就是right处。不过呢,由于跳出循环后,left=rigth,所以返回哪个都是ok的。

 

如果以后有什么关于跳出后对left和right的判断问题迷惑的,可以仿照上述思想,判断跳出前left和right的状态,然后根据if,else语句判断。

 

posted @ 2022-03-26 17:01  啤酒加点醋  阅读(38)  评论(0)    收藏  举报