二分查找,来一道不是那么复杂的题目


  二分查找的思想和套路已经了然于心了,但是还是有些问题解决不了?这个时候就可能需要多见一些特殊的用例了。这一次再来一种二分查找的特例。

题目描述

峰值元素是指其值大于左右相邻值的元素。

给定一个输入数组 nums,其中 nums[i] ≠ nums[i+1],找到峰值元素并返回其索引。

数组可能包含多个峰值,在这种情况下,返回任何一个峰值所在位置即可。

你可以假设 n u m s [ 1 ] = n u m s [ n + 1 ] = nums[-1] = nums[n+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。

  这个问题也是查找中的一个典型,虽然看起来不是那么难,但是告诉你这是一个二分查找的题目,想出 O ( l g n ) O(lgn) 复杂度的解也是不那么容易。

题目分析

  题目中提到后一个元素不会和前一个元素相同,这一点就保证了峰值实际上是严格的峰值。此外还提到 n u m s [ 1 ] = n u m s [ n + 1 ] = nums[-1] = nums[n+1] = -∞ ,这一条就假设了峰值一定存在。数组中的最大值一定是峰值。所以问题一定是有解的。而且只需要返回一个解,肯定不需要遍历了。
  首先注意到这个问题中数组都不是有序或者局部有序的,如果要使用二分查找,这个时候需要联想二分查找的特性,如何去划分子问题。
  肯定要找到中间元素,但是中间元素的情况如何决定解的位置。找峰值,仍然以上坡和下坡为例。如果中间元素处于上坡的区间,显然峰值出现在右边。如果中间元素处于下坡的区间,则峰值出现在左边。接下来就是判断中间元素到底在上坡还是下坡了。那就比较它和自己后面的元素,到底哪个更大。如果比下一个元素大,就是在下坡,反之则是上坡。(不会出现相等的情况)
  找到了如何划分问题。还需要注意的一点就是,如果自己比下一个元素大,说明在下坡,此时可能自己就是坡顶元素,这个时候二分法的结束位置就需要包括自己的当前位置。

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;
}

  通常的二分查找,需要对比的是中间元素和两端元素的关系,但是实际上也可以对比中间元素和周围元素的关系来查找。因为计算机中的除法都是向下取整,所以当 s t a r t e n d start \neq end 时, m i d + 1 mid +1 一定在区间内,而 m i d 1 mid - 1 则不一定在。这一点也是需要注意的。

相关文章:二分查找的特性及应用
原创文章 50 获赞 79 访问量 107万+

猜你喜欢

转载自blog.csdn.net/m0_38065572/article/details/106078530