博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
Find Peak Element
阅读量:5350 次
发布时间:2019-06-15

本文共 5683 字,大约阅读时间需要 18 分钟。

https://oj.leetcode.com/problems/find-peak-element/

A peak element is an element that is greater than its neighbors.

Given an input array where num[i] ≠ num[i+1], find a peak element and return its index.

The array may contain multiple peaks, in that case return the index to any one of the peaks is fine.

You may imagine that num[-1] = num[n] = -∞.

For example, in array [1, 2, 3, 1], 3 is a peak element and your function should return the index number 2.

Note:

Your solution should be in logarithmic complexity.

解题思路:

这题O(n)的解法很简单,几乎是straightforward。需要注意的就是题目为什么给nuo[-1] = num[n] = -∞这个条件,就是说边界的元素只要比相邻的一个大就可以了。

public class Solution {    public int findPeakElement(int[] num) {        if(num.length == 0){            return -1;        }        if(num.length == 1){            return 0;        }        // if(num.length == 2){        //     return num[0] > num[1] ? 0 : 1;        // }        for(int i = 0; i < num.length; i++){            if(i - 1 < 0){                if(num[0] > num[1]){                    return 0;                }                continue;            }            if(i + 1 > num.length - 1){                if(num[i] > num[i - 1]){                    return i;                }                continue;            }            if(num[i - 1] < num[i] && num[i] > num[i + 1]){                return i;            }        }        return 0;    }}

而题目要求用O(logn)的时间求解,对数时间一般都是用折半查找的方法,也就是用start和end两个指针指向开头和结尾,然后根据一定的条件去用mid更新start或者end。这里困难的地方在于,如果mid不是peak element,如何判断peak element究竟是在mid往左还是往右?

解题的关键一定在这里,让我们再来思考一下。

一段数字没有peak,那么这段数字一定是单调递增或者递减的(这么说并不准确,因为递增的最后一个数字是peak)。这是一个非常重要的性质,想一下,如果mid比左大比右小,也就是说以mid为中点的三个元素是递增的,那么mid的右侧一定有peak element!注意是右侧一定有,左侧可能有可能没有。而题目的要求是,如果有多个peak element,返回任意一个就可以了。

如何理解这个结论?以mid为中点的三个元素是递增的,如果mid的右侧仍然一直递增,那么最后一个元素一定是peak element,因为它大于前一个元素,而num[n] = -∞。如果mid的右侧不是一直递增,那么某一点往后肯定突然变小一次,那么这一点就是我们要找的peak element。所以寻找范围只要在mid往右就可以了!我们并不知道mid的左侧是否有peak element,但这无所谓,已经足够了。

反之,如果以mid为中点的三个元素是递减的,搜索范围就是mid往左。

这样看来,O(logn)的解法已经有了。但是还有几个细节需要考虑。

首先,如果mid比两边都小怎么办?结论是peak element一定在mid往左和mid往右都存在。为什么?假设mid往左是从左至右递增的,那么mid -1就是peak element;如果mid往左是从左至右递减的,num[0]就是peak element。mid往右也同样可以证明。所以这时搜索mid两侧的任意一侧都可以。

第二,和二分查找一样,用mid去更新start和end的值的时候,要注意边界值。在这个问题里写过一个比较容易理解的方法,就是用[start,end)这个区间的开闭性来判断。

public class Solution {    public int findPeakElement(int[] num) {        if(num.length == 0){            return -1;        }        if(num.length == 1){            return 0;        }        if(num.length == 2){            return num[0] > num[1] ? 0 : 1;        }                int start = 0;        int end = num.length;        while(start < end){            int mid = (start + end) / 2;            if(mid - 1 < 0){                if(num[mid] > num[mid + 1]){                    return mid;                }else{                    start = mid + 1;                }                continue;            }            if(mid + 1 > num.length - 1){                if(num[mid] > num[mid - 1]){                    return mid;                }else{                    end = mid;                }                continue;            }            if(num[mid] > num[mid - 1] && num[mid] > num[mid + 1]){                return mid;            }            if(num[mid] > num[mid - 1] && num[mid] < num[mid + 1]){                start = mid + 1;            }            if(num[mid] < num[mid - 1] && num[mid] > num[mid + 1]){                end = mid;            }            if(num[mid] < num[mid - 1] && num[mid] < num[mid + 1]){                start = mid + 1;                // end = mid; //也可以            }        }        return 0;    }}

上面是一个左闭右开的区间,所以初始化时start=0,end=length,循环的条件为start<end,更新值时start=mid + 1,end=mid。

下面是一个左闭右闭的解法。

public class Solution {    public int findPeakElement(int[] num) {        if(num.length == 0){            return -1;        }        if(num.length == 1){            return 0;        }        if(num.length == 2){            return num[0] > num[1] ? 0 : 1;        }                int start = 0;        int end = num.length - 1;        while(start <= end){            int mid = (start + end) / 2;            if(mid - 1 < 0){                if(num[mid] > num[mid + 1]){                    return mid;                }else{                    start = mid + 1;                }                continue;            }            if(mid + 1 > num.length - 1){                if(num[mid] > num[mid - 1]){                    return mid;                }else{                    end = mid - 1;                }                continue;            }            if(num[mid] > num[mid - 1] && num[mid] > num[mid + 1]){                return mid;            }            if(num[mid] > num[mid - 1] && num[mid] < num[mid + 1]){                start = mid + 1;            }            if(num[mid] < num[mid - 1] && num[mid] > num[mid + 1]){                end = mid - 1;            }            if(num[mid] < num[mid - 1] && num[mid] < num[mid + 1]){                start = mid + 1;                // end = mid; //也可以            }        }        return 0;    }}

update 2015/07/12:

一个简单的解法,只需判断mid和后一个元素的大小就可以了。

public class Solution {    public int findPeakElement(int[] nums) {        if(nums.length == 0) {            return -1;        }        int left = 0, right = nums.length - 1;        while(left <= right) {            int mid = (left + right) / 2;            if(left == right) {                return left;            } else if(nums[mid] < nums[mid + 1]) {
          // mid一定不是,因为它已经小于右侧邻居 left = mid + 1; } else {
          // mid有可能是,因为他仍谈大于右侧邻居 right = mid; } } return left; }}

 

转载于:https://www.cnblogs.com/NickyYe/p/4320560.html

你可能感兴趣的文章
Redis常用命令
查看>>
[转载]电脑小绝技
查看>>
windos系统定时执行批处理文件(bat文件)
查看>>
thinkphp如何实现伪静态
查看>>
BZOJ 2243: [SDOI2011]染色( 树链剖分 )
查看>>
BZOJ 1925: [Sdoi2010]地精部落( dp )
查看>>
c++中的string常用函数用法总结!
查看>>
[DLX精确覆盖+打表] hdu 2518 Dominoes
查看>>
SuperMap iServerJava 6R扩展领域开发及压力测试---判断点在那个面内(1)
查看>>
Week03-面向对象入门
查看>>
一个控制台程序,模拟机器人对话
查看>>
Vue 2.x + Webpack 3.x + Nodejs 多页面项目框架(上篇——纯前端多页面)
查看>>
我的PHP学习之路
查看>>
【题解】luogu p2340 奶牛会展
查看>>
对PostgreSQL的 SPI_prepare 的理解。
查看>>
解决响应式布局下兼容性的问题
查看>>
使用DBCP连接池对连接进行管理
查看>>
【洛谷】【堆+模拟】P2278 操作系统
查看>>
hdu3307 欧拉函数
查看>>
Spring Bean InitializingBean和DisposableBean实例
查看>>