LeetCode 33-Search in Rotated Sorted Array(二分,旋转有序数组)

LeetCode 33-Search in Rotated Sorted Array

题干:

给定一个元素各不相同的升序数组 n u m s nums nums,现将 n u m s nums nums k k k位置( k k k未知)旋转(即将从 k k k开始到末尾的部分移到最前面),要求在 l o g ( n ) log(n) log(n)时间内在 n u m s nums nums中寻找元素 t a r g e t target target并返回对应位置(没找到返回 − 1 -1 1)。

Input: nums = [4,5,6,7,0,1,2], target = 0
Output: 4

解:

方法1:

一种思路是这样的:画图可以看到,数组满足二段性:有一个分界点 m m m,在 m m m左侧的数都 ≥ n u m s [ 0 ] \ge nums[0] nums[0](设为true),在 m m m右侧的数都 < n u m s [ 0 ] < nums[0] <nums[0](false),在这个条件下,可认为整个数组是有序的(一侧为true,一侧为false),可以通过二分搜索找到这个分界点 m m m(不妨认为 m m m属于左侧),二分搜索即寻找 f ( x ) = = t r u e f(x)==true f(x)==true的右边界;再通过 t a r g e t target target n u m s [ 0 ] nums[0] nums[0]的关系判断 t a r g e t target target是属于 m m m左侧部分还是 m m m右侧部分,再进行一次二分搜索(寻找一个数)。

int rightBound(vector<int>& nums, int left, int right, int target){
        int ans = nums.size()-1;
        while(left < right){
            int mid = left + ((right - left) >> 1);
            if(nums[mid] >= nums[0]){
                ans = mid;
                left = mid + 1;
            }
            else right = mid;
        }
        return ans;
    }
int binarySearch(vector<int>& nums, int left, int right, int target){
        int ans = -1;
        while(left < right){
            int mid = left + ((right - left) >> 1);
            if(nums[mid] == target){
                ans = mid;
                break;
            }
            else if(nums[mid] < target) left = mid + 1;
            else right = mid;
        }
        return ans;

    }
int search(vector<int>& nums, int target) { 
        // if(nums.size()==1) return nums[0]==target? 0:-1;
        int m = rightBound(nums, 0, nums.size(), nums[0]);

        if(target >= nums[0]) return binarySearch(nums, 0, m+1, target);
        return binarySearch(nums, m+1, nums.size(), target);
    }

方法2:

然而,可以不额外寻找这个分界点,直接对旋转数组进行二分。因为这个旋转数组是部分有序的,所以思路就是寻找部分有序的部分,如果 t a r g e t target target在这部分内,就进行二分搜索;如果不在,说明 t a r g e t target target就在无序的那部分,那么就去无序的那部分继续缩小范围,找到有序的子部分来二分搜索。

这是通过比较 n u m s [ l e f t ] nums[left] nums[left] n u m s [ m i d ] nums[mid] nums[mid]的大小关系来确定怎么缩小范围的。当 n u m s [ l e f t ] ≤ n u m s [ m i d ] nums[left]\le nums[mid] nums[left]nums[mid]时,说明 [ l e f t , m i d ] [left,mid] [left,mid]这部分有序,若 t a r g e t target target位于其中,就在 [ l e f t , m i d − 1 ] [left,mid-1] [left,mid1]这个区间内继续二分,否则说明 t a r g e t target target不在这个区间,就去 [ m i d + 1 , r i g h t ] [mid+1,right] [mid+1,right]区间继续找;当 n u m s [ l e f t ] > n u m s [ m i d ] nums[left]> nums[mid] nums[left]>nums[mid]时,说明 l e f t left left m i d mid mid一定位于分界点 m m m的两侧,那么 [ m i d , r i g h t ] [mid,right] [mid,right]这部分一定有序,就判断 t a r g e t target target是否位于其中,若在,就在 [ m i d + 1 , r i g h t ] [mid+1,right] [mid+1,right]区间内继续二分,否则说明 t a r g e t target target不在这个区间,要去 [ l e f t , m i d − 1 ] [left,mid-1] [left,mid1]区间继续找。

int search(vector<int>& nums, int target) {
        int left = 0, right = nums.size()-1;
        int ans = -1;

        while(left <= right){
            int mid = left + ((right - left) >> 1);
            if(nums[mid] == target){
                ans = mid;
                break;
            }
            if(nums[mid] >= nums[left]){ // [left,mid]这部分是有序的
                if(target >= nums[left] && target < nums[mid]){
                    right = mid - 1;
                }
                else left = mid + 1;
            }
            else{ //说明left,mid一定在分界点两侧,可得[mid, right]这部分是有序的
                if(target > nums[mid] && target <= nums[right]){
                    left  = mid + 1;
                }
                else right = mid - 1;
            } 
        }
       return ans;
    }

方法3:

58

方法1的二段性和方法2的找有序区间的组合,若 n u m s [ m i d ] ≥ n u m s [ 0 ] nums[mid] \ge nums[0] nums[mid]nums[0],说明 m i d mid mid一定位于分界点 m m m左边, [ l e f t , m i d ] [left, mid] [left,mid]区间有序,再看 t a r g e t target target是否位于其中,否则搜索另一半区间;若 n u m s [ m i d ] < n u m s [ 0 ] nums[mid] < nums[0] nums[mid]<nums[0],说明 m i d mid mid一定位于分界点右边, [ m i d , r i g h t ] [mid,right] [mid,right]区间有序,再看 t a r g e t target target是否位于其中,否则搜索另一半区间。
实现思路可参考LeetCode 153-Find Minimum in Rotated Sorted Array

int search(vector<int>& nums, int target) {
        int left = 0, right = nums.size()-1;
        int ans = -1;

        while(left <= right){
            int mid = left + ((right - left) >> 1);
            if(nums[mid] == target){
                ans = mid;
                break;
            }
            if(nums[mid] >= nums[0]){ // 同方法2,只需改动判断条件。满足这个条件说明mid位于分界点左边,[left,mid]这部分是有序的
                if(target >= nums[left] && target < nums[mid]){
                    right = mid - 1;
                }
                else left = mid + 1;
            }
            else{ //mid位于分界点右边,[mid, right]这部分是有序的
                if(target > nums[mid] && target <= nums[right]){
                    left  = mid + 1;
                }
                else right = mid - 1;
            } 
        }
       return ans;
    }
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值