33. 搜索旋转排序数组

整数数组 nums 按升序排列,数组中的值 互不相同 。

在传递给函数之前,nums 在预先未知的某个下标 k(0 <= k < nums.length)上进行了 旋转,使数组变为 [nums[k], nums[k+1], …, nums[n-1], nums[0], nums[1], …, nums[k-1]](下标 从 0 开始 计数)。例如, [0,1,2,4,5,6,7] 在下标 3 处经旋转后可能变为 [4,5,6,7,0,1,2] 。

给你 旋转后 的数组 nums 和一个整数 target ,如果 nums 中存在这个目标值 target ,则返回它的下标,否则返回 -1 。

你必须设计一个时间复杂度为 O(log n) 的算法解决此问题。

示例 1:

输入:nums = [4,5,6,7,0,1,2], target = 0
输出:4

示例 2:

输入:nums = [4,5,6,7,0,1,2], target = 3
输出:-1

示例 3:

输入:nums = [1], target = 0
输出:-1

提示:

1 <= nums.length <= 5000
-104 <= nums[i] <= 104
nums 中的每个值都 独一无二
题目数据保证 nums 在预先未知的某个下标上进行了旋转
-104 <= target <= 104

这道题目乍一看不难,给他遍历一遍不久行了吗?
但是题目有个要求,时间复杂度为O(log n),那遍历的办法就不行了。
那怎么样才能达到O(log n)呢?

看这个题的要素:数组,查找。应该想到二分查找这个办法(想不到的再去复习复习)。

二分查找简单,但是这个题之所以是中等难度而不是简单,就在于它是个旋转数组。

虽然看起来很唬人,但是仔细分析就能发现,旋转之后,数组的一半总是有序上升,一半还是旋转数组。(想不明白可以在纸上写一写,很容易发现规律)

发现规律之后就很简单了,我们可以在有序的一半执行二分查找,在另一半执行递归或者迭代,因为另一半的数组形式跟原数组是一样的嘛。

代码:

class Solution {
private:
    //二分查找
    int searchAscend(vector<int>& nums, int left, int right, int target)
    {
        int res = -1;
        //当target不在查找范围内,返回-1
        if(target<nums[left] || target>nums[right])
            return res;
        else
        {
            while(left<=right)
            {
                int mid = left+(right-left)/2;
                if(target==nums[mid])
                {
                    res = mid;
                    break;
                }
                else if(target>nums[mid])
                {
                    left=mid+1;
                }
                else if(target<nums[mid])
                {
                    right=mid-1;
                }
            }
        }
        return res;
    }
    //递归调用,因为旋转之后,必有半边是有序升,有半边是跟原数组一样旋转形状
    int searchRevolve(vector<int>& nums, int left, int right, int target)
    {
        int mid = left+(right-left)/2;
        int res = -1;
        /*剪枝:
        1.当target == nums[mid]时返回;
        2.当target != nums[mid]且left == right,即数组只有一个元素时,返回-1;
        3.当target != nums[mid]且mid == left,即数组只有两个元素时,判断target == nums[right],相等返回right,否则返回-1;
        */
        if(target == nums[mid]) return mid;
        else if(left == right) return res;
        else if(mid == left)
        {
            if(target == nums[right]) return right;
            else return res;
        }
        //当左半边时升序时
        if(nums[left] < nums[mid])
        {
            //左半边调用二分查找
            res = searchAscend(nums, left, mid-1, target);
            if(res!=-1) return res;
            else
            {
                //右半边递归调用
                res = searchRevolve(nums, mid+1, right, target);
            }
        }
        //当右半边时升序时
        else if(nums[mid] < nums[right])
        {
            //右半边二分查找
            res = searchAscend(nums, mid+1, right, target);
            if(res!=-1) return res;
            else
            {
                //左半边递归调用
                res = searchRevolve(nums, left, mid-1, target);
            }
        }
        return res;
    }
public:
    int search(vector<int>& nums, int target) {
        if(nums.size()==1)
        {
            return nums[0]==target?0:-1;
        }
        int left=0, right=nums.size()-1;
        return searchRevolve(nums, left, right, target);
    }
};

我的代码看起来挺复杂,思路其实简单。

ac之后看了看评论区的大佬们,发现果然有很牛逼的写法
极简 Solution

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值