二分查找,来一道不是那么复杂的题目
二分查找的思想和套路已经了然于心了,但是还是有些问题解决不了?这个时候就可能需要多见一些特殊的用例了。这一次再来一种二分查找的特例。
题目描述
峰值元素是指其值大于左右相邻值的元素。
给定一个输入数组 nums,其中 nums[i] ≠ nums[i+1],找到峰值元素并返回其索引。
数组可能包含多个峰值,在这种情况下,返回任何一个峰值所在位置即可。你可以假设 。
示例 1:
输入:
nums = [1,2,3,1]
输出: 2
解释: 3 是峰值元素,你的函数应该返回其索引 2。示例 2:
输入: nums = [1,2,1,3,5,6,4]
输出: 1 或 5
解释: 你的函数可以返回索引 1,其峰值元素为 2;
或者返回索引 5, 其峰值元素为 6。
这个问题也是查找中的一个典型,虽然看起来不是那么难,但是告诉你这是一个二分查找的题目,想出 复杂度的解也是不那么容易。
题目分析
题目中提到后一个元素不会和前一个元素相同,这一点就保证了峰值实际上是严格的峰值。此外还提到
,这一条就假设了峰值一定存在。数组中的最大值一定是峰值。所以问题一定是有解的。而且只需要返回一个解,肯定不需要遍历了。
首先注意到这个问题中数组都不是有序或者局部有序的,如果要使用二分查找,这个时候需要联想二分查找的特性,如何去划分子问题。
肯定要找到中间元素,但是中间元素的情况如何决定解的位置。找峰值,仍然以上坡和下坡为例。如果中间元素处于上坡的区间,显然峰值出现在右边。如果中间元素处于下坡的区间,则峰值出现在左边。接下来就是判断中间元素到底在上坡还是下坡了。那就比较它和自己后面的元素,到底哪个更大。如果比下一个元素大,就是在下坡,反之则是上坡。(不会出现相等的情况)
找到了如何划分问题。还需要注意的一点就是,如果自己比下一个元素大,说明在下坡,此时可能自己就是坡顶元素,这个时候二分法的结束位置就需要包括自己的当前位置。
C++代码
int findPeakElement(vector<int>& nums) {
int start = 0, end = nums.size() -1;
while (start < end) {
int mid = (start + end) >> 1;
if (nums[mid] > nums[mid + 1]) { // 下坡
end = mid;
}
else { // 上坡
start = mid + 1;
}
}
return start;
}
通常的二分查找,需要对比的是中间元素和两端元素的关系,但是实际上也可以对比中间元素和周围元素的关系来查找。因为计算机中的除法都是向下取整,所以当 时, 一定在区间内,而 则不一定在。这一点也是需要注意的。