力扣第三十三题-搜索旋转排序数组

这是我参与更文挑战的第30天,活动详情查看: 更文挑战

前言

力扣第三十三题 搜索旋转排序数组 如下所示:

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

一、思路

题目很长,可能一时间会看懵掉,所以要多看几遍才能明白什么意思。

题目中有两个关键信息:

  • 原数组 nums 为升序且值互不相同
  • 实际输入的 nums 为旋转过后的,也就是说只要旋转点不在下标0,nums 就是一个先升序再降序的排列

了解到关键信息后,就可以使用 二分法 来快速找到 target 对应的位置。在迭代的过程中,会有以下几种情况

  1. nums[mid] == target,直接返回下标即可
  2. nums[mid] < nums[right] 说明此时从 mid 向右边一定是升序(因为nums是从某个位置旋转得到的,如果中间值小于最右边的值只能说明 mid 落在了旋转点上或右边)
  3. nums[mid] > nums[right] 此时从 left 到 mid 一定是升序的

举个例子

此处一示例1中的 nums = [4,5,6,7,0,1,2], target = 0 作为例子

二分法具体的步骤如下所示:

  1. 初始化值 left = 0right = 6
  2. 第一步 mid = (left + right)/2 = 3 ,此时 nums[mid] > nums[right], 7 > 2 说明此时 0 ~ mid 是升序的。又因为 target < nums[mid], 0 < 7 说明值可能在右边,所以 left = mid + 1 = 4
  3. 第三步 mid = (left + right)/2 = 5,此时 nums[mid] < nums[right], 1 < 2 说明此时从中间向右一定是升序的。又因为 target < nums[mid], 0 < 1 说明值可能在左半边,所以 right = mid -1 = 4
  4. 第四步 mid = (left + right)/2 = 4,此时正好 nums[4] == target, 4 == 4,返回下标 4 作为结果

二、实现

实现代码

实现代码与思路中保持一致,主要就是要知道该舍弃哪一边来减少遍历的次数,二分法的时间复杂度为 O(logN)

    /**
     * 二分法
     */
    public int search(int[] nums, int target) {
        int len = nums.length;
        int left = 0;   // 左边界
        int right = len -1; // 右边界
        while (left <= right) {
            int mid = (right + left) / 2;
            if (nums[mid] == target) {
                return mid;
            }
            // 右半边为升序
            else if (nums[mid] < nums[right]) {
                if (nums[mid] < target && target <= nums[right]) {
                    // 如果值在右半边,则丢弃左半边
                    left = mid + 1;
                } else {
                    // 其他情况
                    right = mid - 1;
                }
            }
            // 左半边升序
            else {
                if (nums[left] <= target && target < nums[mid]) {
                    // 如果值在左半边,则丢弃右半边
                    right = mid - 1;
                } else {
                    left = mid + 1;
                }
            }
        }
        return -1;
    }
复制代码

测试代码

    public static void main(String[] args) {
        int[] nums = {4,5,6,7,0,1,2};
        int ret = new Number33().search(nums, 0);
        System.out.println(ret);
    }
复制代码

结果

image.png

三、总结

感谢看到最后,非常荣幸能够帮助到你~♥

© 版权声明
THE END
喜欢就支持一下吧
点赞0 分享