欢迎访问LeetCode题解合集
题目描述
假设按照升序排序的数组在预先未知的某个点上进行了旋转。
( 例如,数组 [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
说明:
-
这道题是 寻找旋转排序数组中的最小值 的延伸题目。
-
允许重复会影响算法的时间复杂度吗?会如何影响,为什么?
题解:
二分。
二分不一定必须要有单调性,二分的本质是寻找某种性质的分界点。只要找到某种性质,可以确定目标在区间的前半部分还是后半部分,就可以用二分找到这个分界点。
首先将数组中的元素画在二维坐标系中,横坐标代表下标,纵坐标代表值。
[HTML_REMOVED]
[HTML_REMOVED]
可以看到,虚线左边元素满足 nums[i] >= nums[0]
,虚线右边元素满足 nums[i] <= nums[0]
,二分时,如果 nums[m] == nums[n - 1]
,此时会有疑问:
- 如果
m
在左边水平段,应该往右走 - 如果
m
在右边黑色水平段,应该往左走
也就是说,这个时候根本不知道往哪走?
解决办法很简单,将黑色部分删除,便可以进行二分了。
时间复杂度:$O(logn)$ ,最坏情况为所有元素都相等为$O(n)$
额外空间复杂度:$O(1)$
class Solution {
public:
int findMin(vector<int>& nums) {
int n = nums.size() - 1;
if ( !n ) return nums[0];
if ( nums[n] > nums[0] ) return nums[0];
while ( n && nums[n] == nums[0] ) --n;
if ( !n ) return nums[0];
int l = 0, r = n, m;
while ( l < r ) {
m = (l + r) >> 1;
if ( nums[m] <= nums[n] ) r = m;
else l = m + 1;
}
return nums[l];
}
};
/*
时间:4ms,击败:95.77%
内存:11.8MB,击败:96.81%
*/