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.
You may assume no duplicate exists in the array.
思路:
考虑我们将序列分成两部分,一部分将会是有序的,另外一部分包含最小值。
举例子,一个数字[1,2,3,4,5,6,7](图1), 从离最右值三步的位置旋转,得到[5,6,7,1,2,3,4](图2),假设我们将序列从距离左边k步的位置旋转,得到子序列[AL, AL+1, …, Ak], [Ak+1, …, AR].
迭代条件:
(1)如果有序序列并未被旋转,即AL < AR, 那么AL就是最小值,返回AL;
(2)如果有序序列至少被旋转一步,那么AL必定比AR大;
让我们假设M1是切割点(MID),因为AM1>AR,并且在[AL … AM1]中的每一个元素都大于AR(因为AL>AR,且序列递增),则最小值会在[AM1+1 … AR]中间。(start = mid + 1;)
另外一种情况,如果选择M2为切割点(MID),因为AM2 <=AR, 我们知道[AM2+1 … AR] 中的每一个元素都比AM2大(递增),则最小值在[AL … AM2]中间。(end = mid;)
终止条件:
给出两个例子:A = [1,2] ; B = [2,1]
For A, 1 < 2 => AM < AR, and therefore it will set R = M => R = 0.
For B, 2 > 1 => AM > AR, and therefore it will set L = M + 1 => L = 1.
当L == R时,迭代结束,我们找到最小值,所以循环外条件(start < end),不能取等号。Attention:
1. 迭代终止条件 start == end, 则循环条件不能取等号
while(start < end)2. 判断从start到end是有序序列,则可以直接返回start.
/start到end是有序序列 start是最小值; 由于只旋转一次,start和end之间不会出现更低的值 if(num[start] < num[end]) { return num[start]; }3. 注意根据上面的分析,确定每个迭代的条件和区间的确定,是否包含mid都要注意。
//start到end不满足有序 继续查找较小半部分 最小值在右半部分 if(num[start] <= num[mid]) { start = mid + 1; //num[mid] >= num[start] >= num[end] ,则mid一定不是最小值 可以跳过 } else { end = mid; //num[mid] < num[start], mid有可能是最小值,不能跳过。 }
复杂度:O(log(n))
AC Code:
class Solution { public: int findMin(vector<int> &num) { int n = num.size(); if(n == 0) return 0; int start = 0; int end = n - 1; while(start < end) { int mid = start + (end - start)/2; //start到end是有序序列 start是最小值; 由于只旋转一次,start和end之间不会出现更低的值 if(num[start] < num[end]) { return num[start]; } //start到end不满足有序 继续查找较小半部分 最小值在右半部分 if(num[start] <= num[mid]) { start = mid + 1; //num[mid] >= num[start] >= num[end] ,则mid一定不是最小值 可以跳过 } else { end = mid; //num[mid] < num[start], mid有可能是最小值,不能跳过。 } } return num[start]; } };