[LeetCode] 33、搜索旋转排序数组

题目描述

假设按照升序排序的数组在预先未知的某个点上进行了旋转。搜索一个给定的目标值,如果数组中存在这个目标值,则返回它的索引,否则返回 -1 。

你可以假设数组中不存在重复的元素。算法时间复杂度必须是 O(log n) 级别。

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

解题思路

参考找“旋转数组的最小值”的算法,改进版。(先判断mid指向前后哪一部分)

注意题目中规定了没有重复数字,所以此时只有纯“有序数组”这一种特殊情况。

参考代码

自己提交的“又臭又长”的代码,特判太多,不好。(自己写的代码特判这么多,真是难为自己了)

class Solution {
public:
    int search(vector<int>& rotateArray, int target) {
        int length = rotateArray.size();
        if(length == 0)
            return -1;

        int low = 0, high = length-1;
        while(low <= high){
            // 特殊情况:有序数组(low和high落入同一有序区间)
            if(rotateArray[low] < rotateArray[high])
                return normalBinarySearch(rotateArray, low, high, target);

            int mid = (low + high) >> 1;
            if(rotateArray[mid] == target)
                return mid;
            
            // 这里要加等号,不然[1] 0就通不过
            if(rotateArray[mid] >= rotateArray[low]){
                if(rotateArray[mid] > target && target > rotateArray[high])  // 注意这里严格的条件限制&&  eg: {4,5,6,7,0,1,2}
                    return normalBinarySearch(rotateArray, low, mid, target);
                else
                    low = mid + 1;

            }else if(rotateArray[mid] <= rotateArray[high]){
                if(rotateArray[mid] < target && target < rotateArray[low])   // 注意这里严格的条件限制&&
                    return normalBinarySearch(rotateArray, mid, high, target);
                else
                    high = mid - 1;
            }

        }
        return -1;
    }

    int normalBinarySearch(vector<int> rotateArray, int low, int high, int target){

        while(low <= high){
            int mid = (low + high) >> 1;
            if(rotateArray[mid] == target)
                return mid;
            else if(rotateArray[mid] < target)
                low = mid + 1;
            else
                high = mid - 1;
        }

        return -1;
    }

};

以上代码是自己写的,其中还是有很多小细节debug了好几次才通过。 常规思路就是这样。

大佬代码(上面我提交的代码不用看,又臭又长,当时真是难为自己了。看这个就好,推荐!!!

class Solution {
public:
    int search(vector<int>& nums, int target) {
        int len = nums.size();
        if (len == 0) {
            return -1;
        }

        int left = 0;
        int right = len - 1;
        while (left < right) {
            int mid = left + ((right - left + 1) >> 1);  // 取右中位数
            // 右半部分有序(首先我们要明确:我们需要在有序区间中进行“具体操作”,其它操作丢给else即可)
            if (nums[mid] <= nums[right]) {  // 这里这个等号至关重要(记住“旋转数组”题目,判断nums[mid]和两边界的大小时,要加个等号)!!  不加等号就错了,特例:{3, 1}  3  
                // 使用上取整的中间数,必须在上面的 mid 表达式的括号里 + 1
                // 重要:把比较好写的判断(如 target 落在有序区间的那部分)放在 if 的开头考虑,把剩下的情况放在 else 里面。(即 先写好写的条件)
                if (nums[mid] <= target && target <= nums[right]) {   // 因为右半部分有序,所以可以在这部分区间内进行一些“操作”
                    // 下一轮搜索区间是 [mid, right]
                    left = mid;   // 这里决定了我们的mid要选“右中位数”
                } else {
                    // 只要上面对了,这里不用思考,可以一下子写出来
                    right = mid - 1;
                }

            } else {  // 左半部分有序
                // [left, mid] 有序,但是为了和上一个 if 有同样的收缩行为,(收缩右边界)
                // 我们故意只认为 [left, mid - 1] 有序
                // 当区间只有 2 个元素的时候 int mid = (left + right + 1) >>> 1; 一定会取到右边
                // 此时 mid - 1 不会越界,就是这么刚刚好
                
                if (nums[left] <= target && target <= nums[mid - 1]) {  // 这是大佬写法(还是这么写比较好)
                // if (nums[left] <= target && target < nums[mid]) {  // 这样写也可以
                    // 下一轮搜索区间是 [left, mid - 1]
                    right = mid - 1;  // (保证了)和上面同样的收缩行为
                } else {
                    // 同理,只要上面对了,这里不用思考,可以一下子写出来
                    left = mid;
                }
            }
        }

        // 有可能区间内不存在目标元素,因此还需做一次判断
        if (nums[left] == target) {  
            return left;
        }
        return -1;
    }
};

python版本(可以看看,但不是很推荐)

class Solution:
    def search(self, nums, target):
        size = len(nums)
        if size == 0:
            return -1

        left = 0
        right = size - 1
        while left < right:
            # mid = left + (right - left + 1) // 2
            mid = (left + right + 1) >> 1
						
            # 这里的这个if要是不用等号,就要这么写才行:  if nums[mid] < nums[left]
            # 右半部分有序
            if nums[mid] < nums[left]:      # 这里改为nums[right]就不对了。  特例:[3,1]  3
                if nums[mid] <= target <= nums[right]:
                    left = mid
                else:
                    right = mid - 1
            else:  # 上面的if改为nums[right]不对:因为此时无法保证当不满足上述if时,[left, mid]是有序的
                if nums[left] <= target < nums[mid]:
                    right = mid - 1
                else:
                    left = mid
        # 后处理
        return left if nums[left] == target else -1
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值