21-4-07 力扣每日刷题 搜索旋转排序数组 I&II

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

解法:二次二分

  • 第一次二分:
    根据与num[0]比较来划分,大于等于num[0]的即为数组的前半段,小于num[0]即为数组的后半段。
    查找出旋转点
  • 第二次二分:查找出target

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;
        int r=n-1;
        int mid;
        while (l<r){
            mid=l+r+1>>1;
            if(nums[mid]>=nums[0]){
                l=mid;
            }else{
                r=mid-1;
            }
        }
        if(target>=nums[0]){
            l=0;
        }else {
            l=l+1;
            r=n-1;
        }
        while (l<r){
            mid=l+r+1>>1;    //l+r +1   +1是因为if判断中为<=,为了避免死循环而加。
            if(nums[mid]<=target){
                l=mid;
            }else {
                r=mid-1;
            }
            //下面是不+1 的写法
            //  mid=l+r>>1;
            // if(nums[mid]<target){
            //     l=mid+1;
            // }else {
            //     r=mid;
            // }
        }
        return  nums[r]==target? r:-1;
    }

81. 搜索旋转排序数组 II

整数数组 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

此题是上面的加强版,在数组中会出现重复元素。并且可能出现第一次二分的干扰项,例子如下

2  3  4  5  1  2  2
n=nums.length
l=0
r=n-1
此时num[r]==num[0],即在后半段的数也存在大于等于num[0]的数,所以此时我们需要恢复二段性。所以当
l<r(确保数组不会越界 越界例子 1,1,1,1,1,1)且num[r]==num[0]时,r--。

下附具体代码

public boolean search(int[] nums, int target) {
        int n=nums.length;
        if(n==0)return false;
        if(n==1) return nums[0]== target? true:false;
        int l=0;
        int r=n-1;
        int mid;

        //恢复二段性
        while(l < r && nums[0]==nums[r]) r--;
        //第一次二分 ,查找旋转点
        while (l<r){
            mid=l+r+1>>1;
            if(nums[mid]>=nums[0]){
                l=mid;
            }else{
                r=mid-1;
            }
        }
        int idx=n;
        if(r+1<n) idx=r+1;
        //判断target 在前半段还是后半段
        if(target>=nums[0]){
            l=0;
            r=idx-1;
        }else {
            l=idx;
            r=n-1;
        }
        //第二次二分 ,查找target
        while (l<r){
            mid=l+r+1>>1;    //l+r +1   +1是因为if判断中为<=,为了避免死循环而加。
            if(nums[mid]<=target){
                l=mid;
            }else {
                r=mid-1;
            }
        }
        return  nums[r]==target? true:false;
    }

总结

  1. 有序队列查值,用二分。
  2. 「二分」的本质是两段性,并非单调性。只要一段满足某个性质,另外一段不满足某个性质,就可以用「二分」。

参考 严格 O(logN),一起看清二分的本质


此文章创于本人学习时的记录,如有错误或更优解还请指出

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值