刷题学习记录——leetcode 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 。

示例 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
-10^4 <= nums[i] <= 10^4
nums 中的每个值都 独一无二
题目数据保证 nums 在预先未知的某个下标上进行了旋转
-10^4 <= target <= 10^4

初步思路

对于查找数组中有没有某个元素问题,有一个核武器就是新建另一个数组,遍历原数组值然后将其当作新数组下标,操作对应下标的位置的值,对于要查找的元素,如果新数组中对应下标位置没有变化,那么就可以认为没有这个元素。

本题也是这一个思路,但是注意到数组中的元素是可以出现负数的,范围在-10000到10000之间,于是我们用一个target+10000就将这个范围变成了0-20000之间,最终可以通过全部测试样例哈哈!

缺点是辅助空间消耗太大,属于空间换时间的思路。

class Solution {
    public int search(int[] nums, int target) {
        int MAX = 20000;
        int len = nums.length;
        int[] a=new int[MAX];//动态方法定义
        for(int i=0;i<MAX;i++)
            a[i] = -1;
        
        for(int i=0;i<len;i++)
        {
            a[nums[i]+10000] = i;
        }

        if(a[target+10000] == -1)
            return -1;
        else
            return a[target+10000];

    }
}

看了题解之后

原做法规避了数组的原有特性(部分有序),无序的数组一样可以做,但是题解还是基于这个特性解题。利用了二分查找的思想,数组中一定有一个部分是有序的,另一个可能有序也可能部分有序,此时有序部分用二分查找,无序部分再次一分为有序部分和部分有序部分一直循环进行二分查找,直到找到为止。

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;

        int l = 0,r = n - 1;
        while(l <= r){
            int mid = (l + r) / 2;
            if(nums[mid] == target){
                return mid;
            }
            if(nums[0] <= nums[mid]){
                if(nums[0] <= target && target < nums[mid]){
                    r = mid - 1;
                }else{
                    l = mid + 1;
                }
            }else{
                if(nums[mid] < target && target <= nums[n-1]){
                    l = mid + 1;
                }else{
                    r = mid - 1;
                }
            }
        }
        return -1;
    }
}

可以发现的是,我们将数组从中间分开成左右两部分的时候,一定有一部分的数组是有序的。拿示例来看,我们从 6 这个位置分开以后数组变成了 [4,5, 6] 和 [7, 0, 1, 2] 两个部分,其中左边 [4, 5, 6] 这个部分的数组是有序的,其他也是如此。

这启示我们可以在常规二分查找的时候查看当前 mid 为分割位置分割出来的两个部分 [l, mid] 和 [mid + 1, r]
哪个部分是有序的,并根据有序的那个部分确定我们该如何改变二分查找的上下界,因为我们能够根据有序的那部分判断出 target 在不在这个部分:

如果 [l, mid - 1] 是有序数组,且 target 的大小满足
[\textit{nums}[l],\textit{nums}[mid])[nums[l],nums[mid]),则我们应该将搜索范围缩小至
[l, mid - 1],否则在 [mid + 1, r] 中寻找。 如果 [mid, r] 是有序数组,且 target 的大小满足
(\textit{nums}[mid+1],\textit{nums}[r]](nums[mid+1],nums[r]],则我们应该将搜索范围缩小至
[mid + 1, r],否则在 [l, mid - 1] 中寻找。

作者:LeetCode-Solution
来源:力扣(LeetCode) 著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值