LeetCode 33. Search in Rotated Sorted Array

LeetCode 33. Search in Rotated Sorted Array

题目描述

There is an integer array nums sorted in ascending order (with distinct values).

Prior to being passed to your function, nums is possibly rotated at an unknown pivot index k (1 <= k < nums.length) such that the resulting array is [nums[k], nums[k+1], …, nums[n-1], nums[0], nums[1], …, nums[k-1]] (0-indexed). For example, [0,1,2,4,5,6,7] might be rotated at pivot index 3 and become [4,5,6,7,0,1,2].

Given the array nums after the possible rotation and an integer target, return the index of target if it is in nums, or -1 if it is not in nums.

You must write an algorithm with O(log n) runtime complexity.

Example 1:

Input: nums = [4,5,6,7,0,1,2], target = 0
Output: 4

Example 2:

Input: nums = [4,5,6,7,0,1,2], target = 3
Output: -1

Example 3:

Input: nums = [1], target = 0
Output: -1

思路

这道题目是二分查找的变种,因为数组是有序的,所以可以使用二分查找,但是这道题目的数组是旋转过的,所以需要先找到原数组的起始位置,然后再进行二分查找。

原数组的起始位置可以通过二分查找的方式找到, 原数组的起始位置就是数组中最小的元素的位置。

以数组[4,5,6,7,0,1,2]为例,原数组的起始位置就是0的位置, 而0也是数组中最小的元素。

那么如何找到起始的位置呢?

可以通过二分查找找到数组中间的元素,如果中间元素大于数组的最后一个元素,那么说明原数组的起始位置在中间元素的右边,如果中间元素小于数组的最后元素,那么说明原数组的起始位置在中间元素的左边或者就是中间元素。

以数组[4,5,6,7,0,1,2]为例,中间元素是7,7大于数组的最后一个元素2,所以原数组的起始位置在7的右边,也就是0的位置。

再以数组[6,7,0,1,2,4,5]为例,中间元素是0,0小于数组的最后一个元素5,所以原数组的起始位置在0的左边或者就是0。这里就是0。

如何找到目标元素的位置呢?

我们需要先将原数组扩展, 比如数组[4,5,6,7,0,1,2]扩展为[4,5,6,7,0,1,2,4,5,6,7],其实就是把原数组的起始位置之前的元素复制到数组的后面。

在扩展之后的数组里使用二分查找, 必须将原数组的下标转换为原数组的下标,因为旋转后的数组不是递增的。

假设数组元素个数为n, 数组的起始位置为rot, 数组的最后一位为high, 数组的第一位为low,
那么旋转后的数组的中间元素位置是mid = (low + high) / 2, 真正的中间元素位置是realMid = (mid + rot) % n

可以这样理解, 在[4,5,6,7,0,1,2,4,5,6,7]里, realMid = (rot + (hi + rot) / 2) % n, 也就是 (rot + hi/2) % n, 实际上是(rot + mid) % n

代码

class Solution {
    public int search(int[] nums, int target) {
        int n = nums.length;
        if (n == 0) {
            return -1;
        }
        if (n == 1) {
            return nums[0] == target ? 0 : -1;
        }
        int low = 0;
        int high = n - 1;
        while (low < high) {
            int mid = (low + high) / 2;
            if (nums[mid] > nums[high]) {
                low = mid + 1;
            } else {
                high = mid;
            }
        }
        int rot = low;
        low = 0;
        high = n - 1;
        while (low <= high) {
            int mid = (low + high) / 2;
            int realMid = (mid + rot) % n;
            if (nums[realMid] == target) {
                return realMid;
            }
            if (nums[realMid] < target) {
                low = mid + 1;
            } else {
                high = mid - 1;
            }
        }
        return -1;
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值