1.題目
假設按照升序排序的數組在預先未知的某個點上進行了旋轉。例如,數組 [0,1,2,4,5,6,7] 可能變為 [4,5,6,7,0,1,2] 。
請找出其中最小的元素。
示例 1:
輸入:nums = [3,4,5,1,2]
輸出:1
示例 2:
輸入:nums = [4,5,6,7,0,1,2]
輸出:0
示例 3:
輸入:nums = [1]
來源:力扣(LeetCode)
2.時間複雜度O(n)的解法
2.1 思路
本思路是概括了題目和修改後的題目,暴力破解都是這個方法。
原數組是非遞減的,旋轉後,有兩種情況,數組仍是非遞減的,例如将全部元素放到數組的末尾,它相當于還是原數組,或者有重複項,例如 {2,2,2,2},旋轉後仍是非遞減的,那麼我們傳回第一個元素即可。另一種情況就會出現前一個元素大于後一個元素,那麼後一個元素一定是最小的,也就是我們要的解。
2.2 代碼
int findMin(vector<int>& nums) {
if(!nums.size()) return -1;
for(int i = 0; i < nums.size() - 1; i++) {
if(nums[i] > nums[i + 1])
return nums[i + 1];
}
return nums[0];
}
3.時間複雜度O(logn)的解法
3.1 思路
此思路是假設沒有重複的數。
我們知道,當數組是已經排序好的,我們可以用二分法來查找,其實本題也可以利用二分法來做。
我們找一個中間值,如果這個中間值小于此時的left,那麼就意味着我們要找的數就在left 到 mid 中間,如果中間值大于left,那麼我們要找的值就在mid到right中間,直到縮減到兩個數的時候,此時左指針指向的數一定大于右指針指向的數,mid此時就會在左指針的位置上,我們傳回右指針即可
3.2 代碼
class Solution {
public:
int findMin(vector<int>& nums) {
int left = 0;
int right = nums.size() - 1;
int mid = (left + right) / 2;
if (nums[left] < nums[right])
{
return nums[left];
}
while (left != mid)
{
if (nums[mid] > nums[left])
{
left = mid;
}
else if (nums[mid] < nums[right])
{
right = mid;
}
mid = (left + right) / 2;
}
return nums[right];
}
};
4.修改後的題(允許元素重複)
4.1題目
把一個數組最開始的若幹個元素搬到數組的末尾,我們稱之為數組的旋轉。
輸入一個升序的數組的一個旋轉,輸出旋轉數組的最小元素。
例如數組{3,4,5,1,2}為{1,2,3,4,5}的一個旋轉,該數組的最小值為1。
數組可能包含重複項。
注意:數組内所含元素非負,若數組大小為0,請傳回-1。
樣例
輸入:nums=[2,2,2,0,1]
輸出:0
題目位址
4.2解法
題目中允許出現重複的數字,這就意味着在原來的了解上我們要多一層可能,那就是中間的值等于左側值或者右側的值,舉一個例子[2,2,2,1,1,2],此時mid指向第三個2,最小值在mid的右側;[2,2,2,1,1,2,2,2,2,2,2],此時最小值在mid左側。這樣我們就需要來判斷此時我們是要在左側尋找還是在右側尋找了。但不管怎麼樣,如果相同,我們可以省略掉相同的數的那個指針。
是以分三種情況,當中間的比最右端的小,此時我們讓右指針移動過來,當中間的比右邊打,我們讓左指針移到中間指針的後方,如果相等,就讓右指針移動。
4.3代碼
class Solution {
public:
int findMin(vector<int>& nums) {
int low = 0;
int high = nums.size() - 1;
while (low < high) {
int pivot = low + (high - low) / 2;
if (nums[pivot] < nums[high]) {
high = pivot;
}
else if (nums[pivot] > nums[high]) {
low = pivot + 1;
}
else {
high -= 1;
}
}
return nums[low];
}
};