一起刷算法 # 二分查找 # No.1

题目

153. 寻找旋转排序数组中的最小值 - 力扣(LeetCode) (leetcode-cn.com)

用时

39分钟

结果

 

优化过程

第一次提交未通过,发现问题是因为对于 3 1 2这种,最小值刚好是mid的情况,无法正确寻找,因为它会跳过mid值。

这个时候我还没意识到,这是因为这道题不是标准二分法的原因,而我使用了一般二分的一般形式。

class Solution {
    public int findMin(int[] nums) {
        int low = 0, hi = nums.length - 1;
        if(nums[low] < nums[hi])return nums[low];
        if(nums.length == 2)return nums[0] > nums[1]? nums[1]:nums[0];
        int mid = -1;
        while(low <= hi){
            mid = low + (hi - low) / 2;
            System.out.println(mid);
            if(nums[mid] > nums[0]){
                low = mid + 1;
            }
            else{
                hi = mid - 1;
            }
        }
        return nums[mid];
    }
}

 

第二次提交,这个时候我加入了一个判断条件 

if(nums[mid] < nums[low] && nums[mid] < nums[hi])return nums[mid];

来处理这种mid是最小值的情况,但还是会有新的问题,这个时候我尝试继续加限制条件,但感觉不太对,这算法看起来就太复杂了。于是开始思考为什么不行。

class Solution {
    public int findMin(int[] nums) {
        int low = 0, hi = nums.length - 1;
        if(nums[low] < nums[hi])return nums[low];
        if(nums.length == 2)return nums[0] > nums[1]? nums[1]:nums[0];
        int mid = -1;
        int temp = -1;
        while(low <= hi){
            mid = low + (hi - low) / 2;
            //System.out.print(mid);
            //System.out.print(low);
            //System.out.println(hi);
            if(nums[mid] < nums[low] && nums[mid] < nums[hi])return nums[mid];
            if(nums[low] < nums[hi])return nums[low];
            if(nums[mid] > nums[low]){

                low = mid + 1;
            }
            else{
                hi = mid - 1;
            }
            temp = mid;
        }
        return nums[mid];
    }
}

 

我发现,所有这些问题都是因为mid这个值在遍历时被漏掉了!

 

简单好理解的二分法!
要找这种序列的最小值,只需要思考,最小值到底在哪里? 最小值一定在无序部分中。
这些序列大概可以分为两种情况:
1. 前半部分有序:5 6 7 8/ 9 1 2
2. 后半部分有序:7 8 1 2/ 3 4 5
我们发现,最小值一定在无序部分,那么我们只需要不断寻找无序部分就行了

代码如下,这里有一些问题:
1.如何寻找无序部分?
    通过if(nums[mid] >= nums[low]) 这一句比较来寻找,即中间值和每次二分出来的部分的第一个值比较,这一点比较好理解.
    还需要加上这一句if(nums[low] <= nums[hi])return nums[low];表示剩下的序列全都是有序了,没法再找无序部分,这个时候直接返回第一个值即是最小值。
2.为什么hi = mid 而不是 mid - 1
    在一般二分中,我们可以写mid - 1和mid + 1是因为我们有一句if(nums[mid] == target)break;**即每次我们都check到了mid位置的值,所以我们可以通过位置移动来排除这个值,但这个算法里是没有这句break语句check到mid值的,所以不能-1
 
于是最终版为:
class Solution {
    public int findMin(int[] nums) {
        int low = 0, hi = nums.length - 1;
        if(nums[low] <= nums[hi])return nums[low];
        int mid = -1;
        while(low <= hi){
            mid = low + (hi - low) / 2;
            if(nums[low] <= nums[hi])return nums[low];
            if(nums[mid] >= nums[low]){
                low = mid + 1;
            }
            else{
                hi = mid;  //!!!!!!!!!!!这里不能减去1
            }
        }
        return nums[mid];
    }
}

  

 

 

 
 
posted @ 2021-12-22 22:19  岚鱼yu  阅读(43)  评论(0)    收藏  举报