1. 程式人生 > >Leetcode 162. 尋找峰值

Leetcode 162. 尋找峰值

題目描述:

峰值元素是指其值大於左右相鄰值的元素。

給定一個輸入陣列 nums,其中 nums[i] ≠ nums[i+1],找到峰值元素並返回其索引。

陣列可能包含多個峰值,在這種情況下,返回任何一個峰值所在位置即可。

你可以假設 nums[-1] = nums[n] = -∞

先用暴力求解。由於假定了nums[-1] = nums[n] = -∞,所以nums[-1]到nums[0]已經是上升了,又因為不存在相鄰的兩個數相等,所以只要找到第一個nums[i]>nums[i+1]就可以認為i是答案。

int findPeakElement(vector<int>& nums) {
        int i=0;
        while(i<nums.size()-1 && nums[i]<nums[i+1]) i++;
        return i;
    }

這個演算法時間複雜度O(n),空間複雜度O(1),測試執行時間 4ms。

根據題目提示,存在一個O(log n)的演算法。下意識想到可能是二分查找了。用二分查詢,找到滿足nums[i-1] < nums[i] < nums[i+1]的i即可。

由於假定了nums[-1] = nums[n] = -∞,可以認為區間[0,n)中存在一個點是極大值點,用二分法找出這個極大值點即可。假定任何情況下,lo左邊的函式值小於lo點的函式值,hi點的函式值小於hi左邊的函式值,令mid=(lo+hi)/2,現在要考慮迭代的條件。

1,當lo<hi時,要比較mid,mid-1,mid+1三點的函式值。mid一定是在當前區間裡的,而mid+1不一定,因為mid+1可能等於hi。當mid+1=hi時,得出結論mid函式值>mid+1函式值,這個結論沒有什麼用,因為它既不能把lo提升到mid,也不能把hi縮小到mid。所以轉而考慮mid-1的情形。

2,mid-1也不一定在當前區間中,因為可能出現mid=low,mid-1=low-1。出現這種情況時,必然有hi=lo+1。那麼區間[lo,hi)中只有一個點lo,它就是要找的答案。

3,如果mid-1在當前區間中,也就存在函式值,可以用於比較。若mid-1函式值<mid函式值,則左邊的區間和整個大區間性質相似,必然存在一個極值點,可以把[lo,hi)縮小到[mid,hi);否則的話,就是mid-1函式值>mid函式值,右邊區間和整個區間的性質相似,必然存在一個極值點,可以把[lo,hi)縮小到[lo,mid)。

4,在情形3二分割槽間的過程中,要麼區間長度不變([lo,hi)->[mid,hi)長度可能不變)此時有lo=mid,那麼根據情形2,已經找到了答案退出迴圈;要麼縮小,在經過有限次迭代後(最多n-1次),必然使區間長度縮小為1,得到答案。也就是該演算法一定能找到答案。

程式碼如下:

int findPeakElement(vector<int>& nums) {
        int lo=0,hi=nums.size(),mid;
        while(lo<hi)
        {
            mid=(lo+hi)/2;
            if(mid==lo) return mid;
            else if(nums[mid]<nums[mid-1])
                hi=mid;
            else
                lo=mid;
        }
    }

時間複雜度O(log n),空間複雜度O(1),測試執行時間 4ms。