LC033-LC081-LC153-LC154--搜索旋转排序数组(二分查找)

33. 搜索旋转排序数组

给你一个升序排列的整数数组 nums ,和一个整数 target 。

假设按照升序排序的数组在预先未知的某个点上进行了旋转。(例如,数组 [0,1,2,4,5,6,7] 可能变为 [4,5,6,7,0,1,2] )。

请你在数组中搜索 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

思路

利用二分法

题目要求时间复杂度 O(logn),显然应该使用二分查找。二分查找的过程就是不断收缩左右边界,而怎么缩小区间是关键。

如果数组[未旋转],在此数组中找一个特定的元素target 的过程

  • 若target==nums[mid] 直接返回
  • 若target<nums[mid],则target位于左侧区间[left,mid)中,经right=mid-1;在左侧区间查找
  • 若target>nums[mid],则target位于左侧区间(left,mid]中,经left = mid+1在左侧区间查找

由于数组「被旋转」,所以左侧或者右侧区间不一定是连续的

当元素不重复时,如果 nums[i] <= nums[j],说明区间说明区间 [i,j] 是「连续递增」的

ij 可以重合,所以这里使用的比较运算符是「小于等于」

因此,在旋转排序数组中查找一个特定元素时:

  1. 若 target == nums[mid],直接返回

  2. 若 nums[left] <= nums[mid],说明左侧区间 [left,mid]「连续递增」。此时:

    • 若 nums[left] <= target <= nums[mid],说明 target 位于左侧。令 right = mid-1,在左侧区间查找

    • 否则,令 left = mid+1,在右侧区间查找

  3. 否则,说明右侧区间 [mid,right]「连续递增」。此时:

    • 若 nums[mid] <= target <= nums[right],说明 target 位于右侧区间。令 left = mid+1,在右侧区间查找

    • 否则,令 right = mid-1,在左侧区间查找
      注意:区间收缩时不包含 mid,也就是说,实际收缩后的区间是 [left,mid) 或者 (mid,right]

例图:
在这里插入图片描述

class Solution {
    public int search(int[] nums, int target) {
  int n = nums.length;
        int left = 0;
        int right = n - 1;
        if (n == 0) {
            return -1;
        }
        if (n == 1) {
            return nums[0] == target ? 0 : -1;
        }
        while (left <= right) {

            int mid = (left + right) >> 1;
            // 等号:考虑 left==right,即只有一个元素的情况
            if (nums[mid] == target) {
                return mid;
            }
            // [left,mid] 连续递增
            if (nums[left] <= nums[mid]) {
                if (nums[left] <= target && target < nums[mid]) {
                    // 加等号,因为 left 可能是 target
                    right = mid - 1;
                } else {
                    left = mid + 1;
                }

            } else {
                // [mid,right] 连续递增
                if (nums[mid] < target && target <= nums[n - 1]) {
                    // 加等号,因为 right 可能是 target
                    // 在右侧 (mid,right] 查找
                    left = mid + 1;
                } else {
                    right = mid - 1;
                }
            }
        }
        System.out.println("未找到");
        return -1;
    }
 }

81.搜索旋转排序数组-ii

搜索旋转排序数组 II

难度中等242

假设按照升序排序的数组在预先未知的某个点上进行了旋转。

( 例如,数组 [0,0,1,2,2,5,6] 可能变为 [2,5,6,0,0,1,2] )。

编写一个函数来判断给定的目标值是否存在于数组中。若存在返回 true,否则返回 false

示例 1:

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

示例 2:

输入: nums = [2,5,6,0,0,1,2], target = 3
输出: false

道题是 33 题的升级版,元素可以重复。当 nums[left] == nums[mid] 时,无法判断 target 位于左侧还是右侧,此时无法缩小区间,退化为顺序查找。

left++,去掉一个干扰项,本质上还是顺序查找:

if nums[left] == nums[mid] {
left++
continue
}

代码

 public boolean search(int[] nums, int target) {
        int n = nums.length;
        int left = 0;
        int right = n - 1;
        if (n == 0) {
            return false;
        }
        if (n == 1) {
            return nums[0] == target ? true : false;
        }
        while (left <= right) {
            int mid = left + (right - left) >> 1;

            if (nums[mid] == target) {
                return true;
            }

            if (nums[left] == nums[mid]) {
                left++;
                continue;
            }
            //前半部分有序
            if (nums[left] <= nums[mid]) {
                //target在前半部分
                if (nums[left] <= target && target < nums[mid]) {
                    right = mid - 1;
                } else {
                    left = mid + 1;
                }
            } else {
                //后半部分有序
                //target在后半部分
                if (nums[mid] < target && target <= nums[n - 1]) {
                    left = mid + 1;
                } else {
                    right = mid - 1;
                }

            }
        }
        return false;

    }

153. 寻找旋转排序数组中的最小值

难度中等293

假设按照升序排序的数组在预先未知的某个点上进行了旋转。例如,数组 [0,1,2,4,5,6,7] 可能变为 [4,5,6,7,0,1,2]

请找出其中最小的元素。

示例 1:

输入:nums = [3,4,5,1,2]
输出:1

示例 2:

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

方法一

class Solution {
    public int findMin(int[] nums) {

         int n=nums.length;
        int left=0;
        int right=n-1;
        if (n==0){
            return -1;
        }
        if (n==1){
            return nums[0];
        }
        while (left<=right){// 实际上是不会跳出循环,当 left==right 时直接返回 
            if (nums[left]<=nums[right]){// 如果 [left,right] 递增,直接返回
                return nums[left];
            }
             int mid=(left+right)>>1;
            if (nums[left]<=nums[mid]){// [left,mid] 连续递增,则在 [mid+1,right] 查找
                left=mid+1;
            }else {
                right=mid;// [left,mid] 不连续,在 [left,mid] 查找
            }
        }
        return -1;
    }
}

方法二

 int left=0;
    int right=nums.length-1;
    while(right>left)
    {
        int mid=left+(right-left)/2;
        if(nums[mid]>nums[right])
            left=mid+1;
        else
            right=mid;
    }
    return nums[left];

154. 寻找旋转排序数组中的最小值 II

假设按照升序排序的数组在预先未知的某个点上进行了旋转。

( 例如,数组 [0,1,2,4,5,6,7] 可能变为 [4,5,6,7,0,1,2] )。

请找出其中最小的元素。

注意数组中可能存在重复的元素。

示例 1:

输入: [1,3,5]
输出: 1
示例 2:

输入: [2,2,2,0,1]
输出: 0

class Solution {
    public int findMin(int[] nums) {
       int n = nums.length;
        int left = 0;
        int right = n - 1;
        if (n == 0) {
            return -1;
        }
        if (n == 1) {
            return nums[0];
        }
        while (left <= right) {
            // 实际上是不会跳出循环,当 left==right 时直接返回
        // 这里增加判断 ↓↓↓

            if (nums[left] < nums[right] || left == right) {// 如果 [left,right] 递增,直接返回
                return nums[left];
            }
            int mid =(right + left) >> 1;
            if (nums[left] == nums[mid]) {
                left++;// 无法判断 mid 位于哪一部分,去掉干扰项//有重复的值       
            }
          else if (nums[left] < nums[mid]) {
              // [left,mid] 连续递增,则在 [mid+1,right] 查找
                left = mid + 1;
            } else {
                right = mid;
            }
        }
        return -1;
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值