LeetCode:33. Search in Rotated Sorted Array

题目:

Suppose an array sorted in ascending order is rotated at some pivot
unknown to you beforehand.

(i.e., [0,1,2,4,5,6,7] might become [4,5,6,7,0,1,2]).

You are given a target value to search. If found in the array return
its index, otherwise return -1.

You may assume no duplicate exists in the array.

Your algorithm’s runtime complexity must be in the order of O(log n).
Example 1:

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

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

题目大意:一个有序数组在某个点处旋转了,给一个target,要求出target在数组里面的位置。没有的话,返回-1。

因为时间复杂度要求O(logn),所以不能暴力求解。我们应该想到二分查找,但是这里并不是一个有序数组啊。没关系,我们来试试看。

对于数组nums,我们取它的中间位置mid,相对于数组的旋转点的位置,可以分为以下三种情况:
1.mid在旋转点左边:此时由mid往左的数组是一个升序数组,由mid+1往右的数组是一个经过旋转的有序数组(和nums类似),我们称其为乱序数组
2.mid恰好是旋转点的位置:此时左右两边都是升序数组
3.mid在旋转点右边:此时mid+1往右是有序数组,mid往左是乱序数组
可以自己举个例子就很明了了。

针对以上三种情况,如果target落入某个有序数组中,那么直接二分查找,可以得到结果;如果可能在某个乱序数组中,那么需要递归调用自身函数。

代码:

class Solution {
public:
    int search(vector<int>& nums, int target) {
        if (nums.empty()) return -1;
        return search(nums, 0, nums.size()-1, target);
    }
private:
    int search(const vector<int>& nums, int lo, int hi, int target) {
        if (lo > hi) return -1;
        if (lo == hi) {
            if (nums[lo] == target)
                return lo;
            else return -1;
        }
        int mid = (lo+hi)/2;
        if (nums[lo] <= nums[mid]) {//第一段升序
            if (target >= nums[lo] && target <= nums[mid])//target在第一段中
                return binarySearch(nums, lo, mid, target);
            else if (nums[mid+1] > nums[hi]) //第二段乱序
                return search(nums, mid+1, hi, target);
            else if (target >= nums[mid+1] && target <= nums[hi])      //第二段升序,且target在其中
                return binarySearch(nums, mid+1, hi, target);
            else return -1;//第二段升序且target不在其中
        }
        else {//第一段乱序,说明第二段升序
            if (target >= nums[mid+1] && target <= nums[hi]) //在第二段当中
                return binarySearch(nums, mid+1, hi, target);
            else return search(nums, lo, mid, target);
        }
    }
    
    int binarySearch(const vector<int>& nums, int lo, int hi, int target) {
        if (lo > hi) return -1;
        if (lo == hi) {
            if (nums[lo] == target)
                return lo;
            else return -1;
        }
        int mid = (lo+hi)/2;
        if (nums[mid] == target)
            return mid;
        else if (target >= nums[lo] && target < nums[mid])
            return binarySearch(nums, lo, mid, target);
        else return binarySearch(nums, mid+1, hi, target);
    }
};

上面是我自己的方法,然后我看评论区,找到了一个简洁的方法,下面介绍:
数组可以分为左边和右边,以旋转的那个点为界
可以将一个数与nums.back()相比较来得知它落在哪一边
如果nums[mid]和target在同一边,那么可以根据nums[mid]和target的大小关系来调整范围(和二分查找相同)
如果在不同边,那么我们就会想要进入target所在的那一边,那么就需要比较target和nums.back()的大小了
如果target > nums.back(),说明在比nums.back()大的那一边;否则,在比nums.back()小的那一边

代码:

class Solution {
public:
    int search(vector<int>& nums, int target) {
        if (nums.empty()) return -1;
        int lo = 0, hi = nums.size()-1;
        while (lo < hi) {//每次lo~hi范围都至少会减小1,最终会lo == hi
            int mid = lo+(hi-lo)/2;
            if ((nums[mid]-nums.back()) * (target-nums.back()) > 0) {
                if (nums[mid] < target)
                    lo = mid+1;
                else
                    hi = mid;
            } 
            else if (target > nums.back()) hi = mid;
            else lo = mid+1;//target == nums.back()的情况也得在这
        }
        
        if (nums[lo] == target)
            return lo;
        else
            return -1;
    }
};

猜你喜欢

转载自blog.csdn.net/weixin_43462819/article/details/84396624