题目:
Suppose a sorted array is rotated at some pivot unknown to you beforehand.
(i.e., 0 1 2 4 5 6 7
might become 4
).
5 6 7 0 1 2
Find the minimum element.
The array may contain duplicates.
思路:这道题是Search
in Rotated Sorted Array的扩展。和Find Minimum in Rotated Sorted Array唯一的区别是,这都题目中会有重复的元素出现。在没有重复元素出现时,我们是根据边缘值和中间值比较确定,如何切掉一半元素的,如果加上了重复元素,我们无法判断哪边有序,哪边无序,也就无法确定元素之间的关系。假设原来的数组{1,2,3,3,3,3,3},旋转后得到{3,3,3,3,3,1,2}或者{3,1,2,3,3,3,3},这时我们判断时边缘和中间值都相等,我们并不知道应该截取哪一半,所以我们只能移动边缘一步,直到边缘和中间不在相等或者相遇。这就导致了最坏的情况下,可能每次都只移动一步,总共移动N次,算法的复杂度变成O(N).
这里我们还需要注意一个问题,我们利用右边缘和中间值来作比较,如果A[MID] == A[HI],我们移动HI一步,HI--. 这样最后返回A[lo]就是正确的最小值。但是如果使用左边缘来判断,由于除法的截断性,出现lo == mid, 导致A[lo] == A[mid], 如果我们移动下界lo++, 可能会跳过最小值。如数组{1,3},最后结果会返回A[lo]
= 3. 上界hi就不会受到除法的截断性的影响,出现相等,就是遇到了重复元素这种情况,我们移动hi,不会影响结果。
二分查找思想并不难,可是实现过程中,需要反复考虑多种情况,主要要处理好细节。
复杂度:O(N)
Attention:
1. 移动上界,用上界来判断。
AC Code:
class Solution { public: int findMin(vector<int> &num) { int lo = 0; int hi = num.size() - 1; int mid = 0; while(lo < hi) { int mid = lo + (hi - lo) / 2; if(num[hi] < num[mid]) { lo = mid + 1; } else if(num[hi] > num[mid]) { hi = mid; } else { hi--; } } return num[lo]; } };
面试中这种问题比较常见,现在的趋势是面试官从一个问题出发,然后follow up一些扩展的问题,而且这些问题一般涉及复杂度的改变,所以是面试中的一道好题,更有可能出现,可以结合Find
Minimum in Rotated Sorted Array来复习。