Leetcode-二分搜索-33. 搜索旋转排序数组/81. 搜索旋转排序数组 II/153. 寻找旋转排序数组中的最小值/154. 寻找旋转排序数组中的最小值 II

题目:33. 搜索旋转排序数组

题解:

代码:

class Solution {
public:
    int search(vector<int>& nums, int target) {
        int n = nums.size();
        if(n == 0){return -1;}
        if(n == 1){return nums[0] == target? 0:-1;}
        int l = 0, r = n-1;
        while(l <= r)
        {
            int  mid = (l+r)/2;
            if(nums[mid] == target){return mid;}
            if(nums[mid] >= nums[0])
            {
                if(nums[0] <= target && nums[mid] > target)
                { r = mid - 1; }
                else 
                { l = mid + 1;  }
            }
            else 
            {
                if(nums[n-1] >= target && nums[mid] < target)
                {l = mid+1;}
                else {r = mid-1;}
            }
        }
        return -1;
    }
};

题目:81. 搜索旋转排序数组 II

题解:

方法一:缩小边界

class Solution {
public:
    bool search(vector<int>& nums, int target) {
        int n = nums.size();
        if(n==0){return false;}
        if(n==1){return nums[0]==target;}
        int  l = 0, r = n-1;
        while(l<=r)
        {
            int  mid = (l+r)/2;
            if(nums[mid] == target){return true;}
            if(nums[l] == nums[mid] && nums[mid] == nums[r])
            {
                ++l;
                --r;
            }
            else if(nums[l]  <=  nums[mid])
            {   
                if(nums[l] <= target && target < nums[mid]){r = mid-1;}
                else{l = mid+1;}
            }
            else
            {
                if(nums[mid] < target && target <= nums[r]){l = mid+1;}
                else{r = mid-1;}
            }
        }
        return false;
    }
};

方法二:恢复二段性+查找旋转点

【宫水三叶】详解为何元素相同会导致 O(n),一起看清二分的本质

image.png

class Solution {
public:
    bool search(vector<int>& nums, int target) {
        int n = nums.size();
        if(n==0){return false;}
        if(n==1){return nums[0]==target;}
        int l = 0, r = n - 1;
        // 恢复二段性
        while (l < r && nums[0] == nums[r]) r--;
        // 第一次「二分」:从中间开始找,找到满足 >=nums[0] 的分割点(旋转点)
         //即前一半有序的最后一个数字
        while (l < r) {
            int mid = l + r + 1 >> 1;
            if (nums[mid] >= nums[0]) {
                l = mid;
            } else {
                r = mid - 1;
            }
        }

        // 第二次「二分」:通过和 nums[0] 进行比较,得知 target 是在旋转点的左边还是有边
        if (target >= nums[0]) {
            l = 0;
        } else {
            l = l + 1;
            r = n - 1;
        }
        while (l < r) {
            int mid = l + r >> 1;
            if (nums[mid] >= target) {
                r = mid;
            } else {
                l = mid + 1;
            }
        }
        return nums[r] == target ;
    }
};

其他:

33题二分思路

class Solution {
    public int search(int[] nums, int target) {
        int n = nums.length;
        if (n == 0) return -1;
        if (n == 1) return nums[0] == target ? 0 : -1;

        // 第一次「二分」:从中间开始找,找到满足 >=nums[0] 的分割点(旋转点)
        int l = 0, r = n - 1;
        while (l < r) {
            int mid = l + r + 1 >> 1;
            if (nums[mid] >= nums[0]) {
                l = mid;
            } else {
                r = mid - 1;
            }
        }

        // 第二次「二分」:通过和 nums[0] 进行比较,得知 target 是在旋转点的左边还是有边
        if (target >= nums[0]) {
            l = 0;
        } else {
            l = l + 1;
            r = n - 1;
        }
        while (l < r) {
            int mid = l + r >> 1;
            if (nums[mid] >= target) {
                r = mid;
            } else {
                l = mid + 1;
            }
        }

        return nums[r] == target ? r : -1;
    }
}

题目153. 寻找旋转排序数组中的最小值

题解:

二分法找到旋转点即可

有个问题

当旋转点位于数组中间以及后面位置的时候可以两段法,位于起始位置的时候,两段法会找到数组末尾元素,需要最后判断一下

class Solution {
public:
    int findMin(vector<int>& nums) {
        int n = nums.size();
        if(n==1){return nums[0];}
        if(n==2){return nums[0]  < nums[1]? nums[0]:nums[1];}
        int l = 0, r= n-1;
        //找到符合右图的断点
        while(l<r)
        {
            int mid = (l+r)/2;
            if(nums[mid] >= nums[0])
            {
                l = mid + 1;
            }
            else
            {
                r = mid;
            }
        }
        //判断是不是左图情况
        return nums[0] < nums[l]? nums[0]:nums[l];

    }
};

或者

class Solution {
public:
    int findMin(vector<int>& nums) {
        int n = nums.size();
        if(n==1){return nums[0];}
        if(n==2){return nums[0]  < nums[1]? nums[0]:nums[1];}
        //提前判断
        if(nums[n-1]>nums[0]){return nums[0];}
        int l = 0, r= n-1;
        while(l<r)
        {
            int mid = (l+r)/2;
            if(nums[mid] >= nums[0])
            {
                l = mid + 1;
            }
            else
            {
                r = mid;
            }
        }
        return nums[l];

    }
};

其他:

详细讲解

class Solution {
public:
    int findMin(vector<int>& nums) {
        int left = 0;
        int right = nums.size() - 1;                /* 左闭右闭区间,如果用右开区间则不方便判断右值 */ 
        while (left < right) {                      /* 循环不变式,如果left == right,则循环结束 */
            int mid = left + (right - left) / 2;    /* 地板除,mid更靠近left */
            if (nums[mid] > nums[right]) {          /* 中值 > 右值,最小值在右半边,收缩左边界 */ 
                left = mid + 1;                     /* 因为中值 > 右值,中值肯定不是最小值,左边界可以跨过mid */ 
            } else if (nums[mid] < nums[right]) {   /* 明确中值 < 右值,最小值在左半边,收缩右边界 */ 
                right = mid;                        /* 因为中值 < 右值,中值也可能是最小值,右边界只能取到mid处 */ 
            }
        }
        return nums[left];    /* 循环结束,left == right,最小值输出nums[left]或nums[right]均可 */     
    }
};

题目154. 寻找旋转排序数组中的最小值 II

题解:

有重复数字,首先恢复二段性,然后与右边界比较,中值靠近左边

class Solution {
public:
    int findMin(vector<int>& nums) {
        int n = nums.size();
        int l = 0,r = n-1;
        if(n==1){return nums[0];}
        //恢复二段性
        while(r > l && nums[0]==nums[r]){r--;}
        //二分
        while(l<r)
        {
            //根据153可知与右边r比较是更合适
            //与右边界比较中值靠左
            int mid = l+(r-l)/2;
            //中值小于等于右边界,可能为最小值
            if(nums[mid] <= nums[r])
            {
                r = mid;
            }
            //中值大于右边界,说明中值不可能包含在最小值里
            else
            {
                l = mid+1;
            }
        }
        return nums[l];
    }
};
  • 恢复二段性最差为O(n) 二分复杂度为 O(logn)。整体复杂度为O(n) 。
  • 空间复杂度:O(1)。
相关推荐
©️2020 CSDN 皮肤主题: 撸撸猫 设计师:马嘣嘣 返回首页