1. 题目来源
链接:寻找旋转排序数组中的最小值II
来源:LeetCode
2. 题目说明
假设按照升序排序的数组在预先未知的某个点上进行了旋转。
( 例如,数组 [0,1,2,4,5,6,7] 可能变为 [4,5,6,7,0,1,2] )。
请找出其中最小的元素。
注意数组中可能存在重复的元素。
示例1:
输入: [1,3,5]
输出: 1
示例2:
输入: [2,2,2,0,1]
输出: 0
3. 题目解析
方法一:二分法
这个是[每日一题] 141. 寻找旋转排序数组中的最小值(数组、二分法、分治、多方法) 进阶版。
当数组中存在大量的重复数字时,就会破坏二分查找法的机制,将无法取得 的时间复杂度,又将会回到 。
比如这两种情况:{1, 0, 1, 1, 1}
和 {1, 1, 1, 0, 1}
,
可以发现,当第一个数字和最后一个数字,还有中间那个数字全部相等的时候,二分查找法就崩溃了,因为它无法判断到底该去左半边还是右半边,就无法缩小范围。
针对这种情况,需要打开思路,拿起我们的顺序查找
- 将右指针左移一位(或者将左指针右移一位),略过一个相同数字,这对结果不会产生影响,因为只是去掉了一个相同的数字而已
- 然后对剩余的部分继续用二分查找法,在最坏的情况下,比如数组所有元素都相同,时间复杂度会升到
参见代码如下:
// 执行用时 :4 ms, 在所有 C++ 提交中击败了97.71%的用户
// 内存消耗 :14.5 MB, 在所有 C++ 提交中击败了5.51%的用户
class Solution {
public:
int findMin(vector<int>& nums) {
int left = 0, right = nums.size() - 1;
while (left < right) {
int mid = left + (right - left) / 2;
if (nums[mid] > nums[right])
left = mid + 1;
else if (nums[mid] < nums[right])
right = mid;
else
--right;
}
return nums[right];
}
};
方法二:分治法、递归
思想和前一道大致相同,分治、递归即可,不过写法跟之前那道略有不同,只有在 nums[start] < nums[end]
的时候,才能返回 nums[start]
,等于的时候不能返回。
比如 [3, 1, 3]
这个数组,或者当 start
等于 end
成立的时候,也可以直接返回 nums[start]
,后面的操作跟之前那道题相同,每次将区间 [start, end]
从中间 mid
位置分为两段,分别调用递归函数,并比较返回值,每次取返回值较小的那个即可。
参见代码如下:
// 执行用时 :12 ms, 在所有 C++ 提交中击败了17.42%的用户
// 内存消耗 :14.7 MB, 在所有 C++ 提交中击败了5.15%的用户
class Solution {
public:
int findMin(vector<int>& nums) {
return helper(nums, 0, nums.size() - 1);
}
int helper(vector<int>& nums, int start, int end) {
if (start == end)
return nums[start];
if (nums[start] < nums[end])
return nums[start];
int mid = (start + end) / 2;
return min(helper(nums, start, mid), helper(nums, mid + 1, end));
}
};