【CT】LeetCode手撕—33. 搜索旋转排序数组

题目


1-思路

1-1 模式识别:

  • 模式1 ——>**O(log n)**:使用 O(log n) 时间复杂度算法查找到目标值,因此想到的算法是二分算法。
  • 模式2 ——> 旋转数组:旋转数组无法通过一次二分找到目标值,因此需要两次二分。

1-2 二分模板 && 本质

  • 根据二分查找的过程,二分模板分两种情况。
    • 一种是二分出 红色边界 target / x ,条件为q[mid]<=x ——> mid = (l+r+1)/2
    • 一种是二分出 绿色边界 target / x ,条件为q[mid]>=x——> mid = (l+r)/2

image.png

二分红色边界

  • 结果收集 r
while(l<r){
    int mid = (l+1+r)/2;
    if(q[mid]<=x) l=mid;
    else r = mid-1;
}

二分绿色边界

  • 结果收集 l
while(l<r){
    int mid = (l+r)/2;
    if(q[mid]>=x) r=mid;
    else l = mid+1;
}
  • 二分的本质在于让 二分的区间满足一个性质。

1-3 本题思路

  • 本题提供的旋转数组的分布如下,因此需要分别使用两次二分

image.png

①二分出第一个区间

    1. 答案区间 l = 0, r = nums.length-1
    1. 利用二分性质,使得 前半段的性质 满足 nums[mid] >= nums[0],二分出旋转点
    1. 区间划分
    • 若满足 nums[mid] >= nums[0] 此时 答案肯定在 [mid,r]
    • 否则答案在 [0,mid-1] 中,因为是 r = mid-1 ,此时 mid 初始化为 mid=(l+r+1)/2
    // 1. 第一次二分找出 分界点
    // 二分本质,找到前半段 满足 nums[mid] >= nums[0]
    int l = 0,r = nums.length-1;
    while(l<r){
        int mid = (l+r+1)/2;
        if(nums[mid] > nums[0]) {
            l = mid;
        }
        else {
            r = mid-1;
        }
    }

②判断 target 在哪个区间

    // 2.判断二次二分边界
    if(target >= nums[0]) 
    {
        l = 0;
    }
    else {
        l=r+1 ;
        r = nums.length-1;
    }

③利用二分性质

        // 3.第二次二分
        while(l<r){
            int mid = (l+r+1)/2;
            if(nums[mid]<=target) l=mid;
            else r = mid-1;
        }
        return nums[r] == target ? r:-1;

2- 实现

⭐33. 搜索旋转排序数组——题解思路

:::info

  • 1. 利用二分性质,二分出分界点
    • 二分本质是使得一个区间满足性质
    • 首次二分使得 **nums[mid]** 满足 **nums[mid]>=nums[0]** 的性质
    • **if(nums[mid] >= nums[0])** 此时 结果在 **[mid,r]** 因此 ——> **l = mid**
    • 否则 r = mid-1 ,根据判断 **mid =(l+r+1)/2**
  • 2. 利用边界找出二次二分边界
    • **if(target >= nums[0]) ——> l = 0;**
    • **else l = mid+1; r = nums.length-1**
  • 3. 二次二分
    • 利用二分红色边界模板
      :::
class Solution {
    public int search(int[] nums, int target) {
        // 1. 第一次二分找出 分界点
        // 二分本质,找到前半段 满足 nums[mid] >= nums[0]
        int l = 0,r = nums.length-1;
        while(l<r){
            int mid = (l+r+1)/2;
            if(nums[mid] > nums[0]) {
                l = mid;
            }
            else {
                r = mid-1;
            }
        }
        // 2.判断二次二分边界
        if(target >= nums[0]) 
        {
            l = 0;
        }
        else {
            l=r+1 ;
            r = nums.length-1;
        }
        
        // 3.第二次二分
        while(l<r){
            int mid = (l+r+1)/2;
            if(nums[mid]<=target) l=mid;
            else r = mid-1;
        }
        return nums[r] == target ? r:-1;
    }
}

3- ACM实现

public class findTraversalNums {

    public static int findT(int[] nums,int target){
        int l = 0 , r = nums.length;
        // 1.首次二分
        while(l<r){
            int mid = (l+r+1)/2;
            if(nums[mid] >= nums[0]) l=mid;
            else r=mid-1;
        }

        // 2. 划分区间
        if(target >= nums[0]) l = 0;
        else{
            l = r+1;
            r = nums.length-1;
        }

        // 2.二次二分
        while(l<r){
            int mid = (l+r+1)/2;
            if(nums[mid] <= target){
                l = mid;
            }else{
                r = mid-1;
            }
        }
        return target == nums[r] ? r:-1;
    }

    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);
        System.out.println("输入旋转数组长度");
        int n = sc.nextInt();
        int[] nums = new int[n];
        for(int i = 0 ; i < n;i++){
            nums[i] = sc.nextInt();
        }
        System.out.println("输入目标值");
        int target = sc.nextInt();
        System.out.println("目标下标为"+findT(nums,target));
    }
}

  • 4
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值