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,它就是要找的答案。

扫描二维码关注公众号,回复: 4329210 查看本文章

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。

猜你喜欢

转载自blog.csdn.net/liusiweix/article/details/84680660