0%

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

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

题目描述:

给出一个长度为 $n$ 的数组,预先按照升序排列,在下标 $k$ ,经过旋转之后得到新的数组 $[nums[k], …, nums[n-1], nums[0],…, nums[k-1]]$ 。给出一个可能存在重复元素值的数组 nums,找出其中最小的元素。尽可能减少整个过程的操作步骤。与153.寻找旋转排序数组中的最小值类似,但是存在重复元素。

数据范围:

$1\le n \le 5000$

题解:

旋转之后的数组满足,观察发现前一段的都大于等于 $nums[0]$ ,后一段的都小于等于 $nums[0]$ 。或者前一段的都大于等于 $nums[n-1]$ ,后一段的都小于等于 $nums[n-1]$ 。可以根据这个进行二分搜索。

image-20230510152941583

不相等的情况和前一题类似,不过如果相等的时候即 $nums[mid] = nums[r]$ ,这时需要 $r=r-1$ 。因为 $nums[r]$ 的值已经留在了 $nums[mid]$ ,后续的搜索过程不会错过该值。相等的时候并不能确定最优解在左侧或是在右侧,只能确定 $r$ 可以删掉了。

只有首尾存在相等值时才会出现判断困难的情况,也可以先移动 $r$ 直到 $nums[r]\neq nums[0]$ 。

代码:

分两段

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
class Solution
{
public:
int findMin(vector<int> &nums)
{
int n = nums.size();
int l = 0, r = n - 1, mid;
while (l < r)
{
mid = l + r >> 1;
if (nums[mid] < nums[r])
{
r = mid;
}
else if (nums[mid] > nums[r])
{
l = mid + 1;
}
else if (nums[mid] == nums[r])
{
r--;
}
}
return nums[l];
}
};

分三段

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
class Solution
{
public:
int findMin(vector<int> &nums)
{
int n = nums.size();
int l = 0, r = n - 1, mid, ans = -1;
while (l <= r)
{
mid = l + r >> 1;
if (nums[mid] < nums[r])
{
ans = mid;
if (mid - 1 >= l && nums[mid - 1] <= nums[mid])
r = mid - 1;
else
break;
}
else if (nums[mid] > nums[r])
{
l = mid + 1;
}
else if (nums[mid] == nums[r])
{
ans = mid;
r--;
}
}
return nums[ans];
}
};