力扣 33. 搜索旋转排序数组

题目说明

整数数组 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.length <= 5000
    • -10^4 <= nums[i] <= 10^4
    • nums 中的每个值都 独一无二
    • 题目数据保证 nums 在预先未知的某个下标上进行了旋转
    • -10^4 <= target <= 10^4

示例

示例 1:

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

示例 2:

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

示例 3:

1
2
输入:nums = [1], target = 0
输出:-1

笔者理解

此题是一道数组算法问题,在力扣题库中被定义为中等题。

解法

当笔者阅读完此题后,采用两次二分的方式,先二分找出原数组的左边界,方法见寻找旋转排序数组中的最小值II,再以左右边界正常二分,让我们来看看具体如何实现的吧。

实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
class Solution {
/*
* 数组
* 二分
*/
public int search(int[] nums, int target) {
int n = nums.length;
if (n == 1) {
return nums[0] == target? 0: -1;
}

int left = 0;
int right = n - 1;

// 当左边界不小于左边界,说明此时数组已经发生了旋转
// 此时二分寻找原数组的右边界
if (nums[left] >= nums[right]) {
// 因为旋转过的数组,左半部分元素总是大于等于右半部分的元素
while (left < right) {
int mid = left + (right - left) / 2;

// 中间元素大于右边界,说明此时 mid 位于左半部分,直接舍去前面的部分
if (nums[mid] > nums[right]) {
left = mid + 1;
}
// 此时位于右半部分
else if (nums[mid] < nums[right]) {
right = mid;
}
// 等于右边界,此时将 right 减一,去重
else {
right--;
}
}
}

// 右边界
right = left - 1 + n;

// 正常二分,要注意取余,避免越界
while (left < right) {
int mid = left + (right - left) / 2;

if (nums[mid % n] == target) {
return mid % n;
}
else if (nums[mid % n] < target){
left = mid + 1;
}
else {
right = mid - 1;
}

}

return nums[left % n] == target? left % n: -1;
}
}

时间和空间效率还行,可见此解法还比较适合此题。

image-20210908101213216

总结

本题是今天的一题,难度是为中等,感兴趣的朋友都可以去尝试一下,此题还有其他更多的解法,朋友们可以自己逐一尝试。