原題鏈接:https://leetcode.com/problems/find-minimum-in-rotated-sorted-array-ii
我在github上的leetcode倉庫:https://github.com/cooljacket/leetcodes
題意
這道題的第一個版本已經寫了博客:http://blog.csdn.net/Jacketinsysu/article/details/52299090
第二版本只是多了一個限制:數組裏可以有重複的元素
思路
用low,high和mid分別表示左端、右端和區間中點。
如下圖所示,當nums[mid] == nums[high]爲true時,該怎麼辦呢?這個時候,最小值可能位於區間[mid1, high]上,也可能位於[low, mid2]上。
《劍指offer》的策略
在區間[low, high]上線性查找,這的確不會錯過,也可行,不過寫起來麻煩,代碼長了好多行。
我的策略
我的方法也是線性查找,不過不是在[low, high]之間,而是簡單–high就好了!
首先,–high是否能夠解決這個問題?相當於從右邊縮減查找區間的範圍,這確實是可行的,也不會遺漏。
其次,爲什麼不++low呢?舉個例子:{4, 4, 4, 4, 0, 1, 1}
第一次查找:[0, 6],mid=3,nums[3] = 4 > nums[high],所以low變成mid+1=4;
第二次查找:[4, 6],mid=5,nums[5] = 1 == nums[high],這個時候++low嗎????那查找區間就變成[5, 6]了,錯過了最小值了。。。
那到底是爲什麼不能++low呢?原因在於我們是拿nums[mid]和nums[high]去做比較的,跟nums[low]沒有一毛錢的直接關係,所以不能用右區間的情況來決定左區間的動態。
說直觀點,就是,比如張三偷東西了,警察來了,人贓俱獲,然後把李四給抓走了,你說冤不冤,哈哈~
性能
最後,評價一下我這個方法的性能,最差的情況也是O(n)的,不過平均情況要比上面的策略好,至於爲什麼就不用多說了吧?(因爲不是一遇到相等的情況就“無腦線性找”,而是進一步,看看能不能繼續做折半查找,見下面的代碼)。
代碼
豬一般的優雅,哈哈!
class Solution {
public:
int findMin(vector<int>& nums) {
int low = 0, high = nums.size() - 1, mid;
while (low < high) {
mid = (low + high) >> 1;
if (nums[mid] > nums[high])
low = mid + 1;
else if (nums[mid] < nums[high])
high = mid;
else // 沒錯,就是這麼簡單!
--high;
}
return nums[low];
}
};
╭︿︿︿╮
{/ o o /}
( (oo) )
︶ ︶︶
另,如果要跟《劍指offer》裏的做法一樣的話,也行,得寫這……麼長:
class Solution {
public:
int findMin(vector<int>& nums) {
int low = 0, high = nums.size() - 1, mid;
while (low < high) {
mid = (low + high) >> 1;
if (nums[mid] == nums[low] && nums[mid] == nums[high])
return linearSearch(nums, low, high);
if (nums[mid] > nums[high])
low = mid + 1;
else if (nums[mid] < nums[high])
high = mid;
else
--high;
}
return nums[low];
}
private:
int linearSearch(const vector<int>& nums, int low, int high) {
int minNum = nums[low];
for (int i = low+1; i <= high; ++i) {
if (nums[i] < minNum)
minNum = nums[i];
}
return minNum;
}
};