LeetCode33 搜索旋转排序数组

问题描述:假设按照升序排序的数组在预先未知的某个点上进行了旋转。
( 例如,数组 [0,1,2,4,5,6,7] 可能变为 [4,5,6,7,0,1,2] )。
搜索一个给定的目标值,如果数组中存在这个目标值,则返回它的索引,否则返回 -1 。
例:输入: nums = [4,5,6,7,0,1,2], target = 0;输出: 4
注:
你可以假设数组中不存在重复的元素。
你的算法时间复杂度必须是 O(log n) 级别
思路

  • 二分查找

由于为升序数组旋转后形成,利用此结构,分析所有情况
令left指向查找序列最左元素,right为最右,mid为中间元素

  1. 当nums[left] < nums[right]时,此时未旋转,有两种情况。
  • nums[left] ≤ target ≤ nums[mid],向前寻找
  • nums[mid] ≤ target ≤ nums[right],向后寻找
  1. 当nums[left] > nums[right]时,发生旋转,有四种情况。
  • nums[left] > nums[mid],旋转在前
  • nums[mid] ≤ target ≤ nums[right],向后寻找
  • target ≤ nums[mid] or target ≥ nums[left],向前寻找
  • nums[left] < nums[mid],旋转在后
  • nums[left] ≤ target ≤ nums[mid],向前寻找
  • nums[mid] ≤ target or nums[left] ≥ target,向后寻找

总结来看,向前寻找共有三条,其余情况为向后寻找

  • nums[left] ≤ target ≤ nums[mid]
  • -----------------target ≤ nums[mid] < nums[left]
  • ----------------------------nums[mid] < nums[left] ≤ target

由此可得出三条判断条件:nums[left] < target;target < nums[mid];nums[mid] < nums[left],那么上述向前查找的情况对应任意两条判断情况成立。即若上述条件两条成立,向前查找,否则向后查找。(三条件包含所有情况,故不可能同时为假)
于是判断可转化为异或操作(1 ^ 2 ^ 3)(两条成立时为假,其余为真),进行边界的移动判断。

  • 二次查找

采用两次二分查找,第一次找到偏移次数,第二次基于偏移利用常规二分查找出给定元素。

//java
//二分查找
class Solution {
    public int search(int[] nums, int target) {
        int left = 0;
        int right = nums.length - 1;
        while(left <= right){
            int mid = (left+right) / 2;
            if(nums[mid] == target) return mid;
            if((target >= nums[left]) ^ (nums[left] > nums[mid]) ^ (nums[mid] >= target))
                left = mid + 1;
            else
                right = mid - 1;
        }
        return -1;
    }
}
//二次二分
class Solution {
    public int search(int[] nums, int target) {
        int len = nums.length;
        if(len == 0) return -1;
        int x = delta(nums, 0, len-1);
        int i = 0;
        int j = len-1;
        while(i <= j){
            int mid = (i+j) / 2;
            int tmid = (mid + x)%len;//基于未移动数组的位置
            if(nums[tmid] == target)
                return tmid;
            else if(nums[tmid] > target)
                j = mid - 1;
            else
                i = mid + 1;
        }
        return -1;
    }
    public int delta(int[] nums, int left, int right){//找到移位次数
        int mid = (left + right) / 2;
        if(nums[mid] > nums[(mid+1)%nums.length])//一旦该数下一元素更大,找到原数组末尾元素
            return (mid+1)%nums.length;
        else if(nums[mid] < nums[right])
            return delta(nums, left, mid);
        else if(nums[mid] > nums[right])
            return delta(nums, mid, right);
        else
            return 0;
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Leo木

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值