LeetCode 搜索旋转排序数组/寻找旋转排序数组中的最小值[二分法]

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

搜索旋转排序数组(33)

题目描述

整数数组 nums 按升序排列,数组中的值 互不相同

在传递给函数之前,nums 在预先未知的某个下标 k0 <= 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

**进阶:**你可以设计一个时间复杂度为 O(log n) 的解决方案吗?

思路分析

这道题目可以这样进行分解,首先判断 mid 是否等于目标值,如果是则直接返回;判断左边有序还是右边有序,再根据具体的条件进行判断。

二分查找有一定的套路,首先定义 low 和 high,while 循环的判断条件一直是 low <= high,对于 low == high,必要的时候特殊处理,在 while 内部补充,返回值永远是 mid;low、high 的更新永远是 low = mid + 1 和 high = mid – 1;

对于非确定性的查找,使用前后探测法,来确定搜索区间。先处理命中情况,再处理左右半部分查找的情况。

代码展示

解法一:时间复杂度是O(logn){O(logn)},空间复杂度是O(1){O(1)}

public int search(int[] nums, int target) {
        //nums = [4,5,6,7,0,1,2], target = 6
        int low = 0;
        int high = nums.length - 1;
        while (low <= high){
            int mid = low + (high - low)/2;
            if (nums[mid] == target){
                return mid;
            } else if (nums[low] <= nums[mid]){ //左边有序
                if (target >= nums[low] && target < nums[mid]){
                    high = mid - 1;
                } else {
                    low = mid + 1;
                }
            } else { //右边有序
                if (target > nums[mid] && target <= nums[high]){
                    low = mid + 1;
                } else {
                    high = mid - 1;
                }
            }
        }
        return -1;

    }
复制代码

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

题目描述

已知一个长度为 n 的数组,预先按照升序排列,经由 1n旋转 后,得到输入数组。例如,原数组 nums = [0,1,2,4,5,6,7] 在变化后可能得到:

  • 若旋转 4 次,则可以得到 [4,5,6,7,0,1,2]
  • 若旋转 7 次,则可以得到 [0,1,2,4,5,6,7]

注意,数组 [a[0], a[1], a[2], ..., a[n-1]] 旋转一次 的结果为数组 [a[n-1], a[0], a[1], a[2], ..., a[n-2]]

给你一个元素值 互不相同 的数组 nums ,它原来是一个升序排列的数组,并按上述情形进行了多次旋转。请你找出并返回数组中的 最小元素

进阶

  • 你可以在 O(n log n) 时间复杂度和常数级空间复杂度下,对链表进行排序吗?

示例 1:

输入:nums = [3,4,5,1,2]
输出:1
解释:原数组为 [1,2,3,4,5] ,旋转 3 次得到输入数组。
复制代码

示例 2:

输入:nums = [4,5,6,7,0,1,2]
输出:0
解释:原数组为 [0,1,2,4,5,6,7] ,旋转 4 次得到输入数组。
复制代码

示例 3:

输入:nums = [11,13,15,17]
输出:11
解释:原数组为 [11,13,15,17] ,旋转 4 次得到输入数组。
复制代码

提示

  • n == nums.length
  • 1 <= n <= 5000
  • -5000 <= nums[i] <= 5000
  • nums 中的所有整数 互不相同
  • nums 原来是一个升序排序的数组,并进行了 1n 次旋转

思路分析

寻找旋转排序数组中的最小值,如果旋转完还是原来的样子,或者说是没有旋转过,我们可以直接返回最小值(第一个)。然后我们再依照类似上题的解法,先判断左侧有序还是右侧有序。最后对 low == high 进行处理一下。

代码展示

解法一:时间复杂度是O(logn){O(logn)},空间复杂度是O(1){O(1)}

public int findMin(int[] nums) { //输入:nums = [11,13,15,17] 输出:11   //[4,5,6,7,0,1,2]

        int low = 0;
        int high = nums.length - 1;
        //旋转过还是原来的样子,或者没旋转过
        if (nums[0] < nums[high] || high == 0){
            return nums[0];
        }
        int target = nums[nums.length - 1];
        while (low <= high){
            int mid = low + (high - low)/2;
            if (target < nums[mid]){
                low = mid + 1;
            } else if (target > nums[mid]){ //有序的
                high = mid;
                target = nums[mid];
            }
            if (low == high){
                if (target < nums[low - 1]){
                    return target;
                }
            }

        }
        return target;

    }
复制代码

总结

这种对数组进行选择排序题也有一定的套路,就是判断左侧还是右侧有序,然后依赖常用的套路。如下:

二分查找有一定的套路,首先定义 low 和 high,while 循环的判断条件一直是 low <= high,对于 low == high,必要的时候特殊处理,在 while 内部补充,返回值永远是 mid;low、high 的更新永远是 low = mid + 1 和 high = mid – 1;

对于非确定性的查找,使用前后探测法,来确定搜索区间。先处理命中情况,再处理左右半部分查找的情况。

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