LeetCode162. 寻找峰值

题目说了,峰值元素就是大于相邻左右元素的元素,又说了nums[-1] = nums[n] = -∞,所以我们就寻思着,如果nums[0]比nums[1]大,再加上nums[-1]是负无穷,那么nums[0]不就是一个峰值元素了吗。

如果nums[0]不比nums[1]大,那么我们接着把nums[1]和nums[2]比就好了,如果nums[1]大,也可以认为nums[1]是峰值元素。如果nums[1]不比nums[2]大,就继续往后找。

这种做法时间复杂度是O(n).

class Solution {
public:
    int findPeakElement(vector<int>& nums) {
        for(int i = 0; i < nums.size() - 1; ++i) {
            if(nums[i] > nums[i + 1]) {
                return i;
            }
        }
        return 0;
    }
};

题目希望我们的解法是O(logn)时间复杂度的。
那就得用二分了。

一般二分都是在一个有序的数组里查找元素用的,这里数组是无序的,但因为我们只需要找任意一个峰值,所以也是可以用二分进行查找的。

题目说了nums[-1] = nums[n] = -∞,再加上这题所有的元素都是不相同的(题目说的),所以一定有个元素,比相邻的元素都大。
就算数组元素是递增的,那么最后一个元素也是峰值元素(因为nums[n] = -∞),递减同理。
所以题目保证了有解。

二分的过程是这样的,每次二分出中点mid,将nums[mid]和nums[mid + 1]进行比较,如果nums[mid] < nums[mid + 1],那么右半部分肯定有峰值,
因为nums[mid + 1]大于mid,而且nums[n]为负无穷,所以右半部分肯定出现了上升又下降这种情况(即出现峰值),所以我们让left = mid + 1(峰值最小也是nums[mid + 1])。

同理,如果nums[mid] > nums[mid + 1],那么左半部分区间肯定有峰值,因为nums[mid]大于nums[mid + 1],且nums[0]是负无穷,所以从mid + 1往左走,肯定出现了先上升后下降这种情况,
所以我们让right = mid。

每次折半查找,最后就找到了峰值元素。

代码如下:

class Solution {
public:
    int findPeakElement(vector<int>& nums) {
        int left = 0, right = nums.size() - 1;
        while(left < right) {
            int mid = (left + right) >> 1;
            if(nums[mid] < nums[mid + 1]) {
                left = mid + 1;
            } else {
                right = mid;
            }
        }
        return left;
    }
};
posted @ 2020-07-31 22:14  machine_gun_lin  阅读(69)  评论(0编辑  收藏  举报