文章目录
- 题目
- 标题和出处
- 难度
- 题目描述
- 要求
- 示例
- 数据范围
- 解法
- 思路和算法
- 证明
- 代码
- 复杂度分析
题目
标题和出处
标题:寻找峰值
出处:162. 寻找峰值
难度
6 级
题目描述
要求
峰值元素是指其值严格大于左右相邻值的元素。
给定一个长度为 n \texttt{n} n 的整数数组 nums \texttt{nums} nums,找到一个峰值元素并返回其下标。如果数组包含多个峰值,则返回任何一个峰值的下标。
可以假设 nums[-1] = nums[n] = − ∞ \texttt{nums[-1]} = \texttt{nums[n]} = -\infty nums[-1]=nums[n]=−∞。
要求时间复杂度是 O(log n) \texttt{O(log n)} O(log n)。
示例
示例 1:
输入: nums = [1,2,3,1] \texttt{nums = [1,2,3,1]} nums = [1,2,3,1]
输出: 2 \texttt{2} 2
解释: 3 \texttt{3} 3 是峰值元素,返回其下标 2 \texttt{2} 2。
示例 2:
输入: nums = [1,2,1,3,5,6,4] \texttt{nums = [1,2,1,3,5,6,4]} nums = [1,2,1,3,5,6,4]
输出: 1 \texttt{1} 1 或 5 \texttt{5} 5
解释:可以返回峰值 2 \texttt{2} 2 的下标 1 \texttt{1} 1,或者峰值 6 \texttt{6} 6 的下标 5 \texttt{5} 5。
数据范围
- 1 ≤ nums.length ≤ 1000 \texttt{1} \le \texttt{nums.length} \le \texttt{1000} 1≤nums.length≤1000
- -2 31 ≤ nums[i] ≤ 2 31 − 1 \texttt{-2}^\texttt{31} \le \texttt{nums[i]} \le \texttt{2}^\texttt{31} - \texttt{1} -231≤nums[i]≤231−1
- 对于所有有效的 i \texttt{i} i 都有 nums[i] ≠ nums[i + 1] \texttt{nums[i]} \ne \texttt{nums[i + 1]} nums[i]=nums[i + 1]
解法
思路和算法
由于题目要求在长度为 n n n 的数组中寻找峰值的时间复杂度是 O ( log n ) O(\log n) O(logn),因此需要使用二分查找的做法寻找峰值。二分查找的过程中需要判断可能存在峰值的下标范围,缩小查找的下标范围。
考虑数组 nums \textit{nums} nums 中的两个相邻下标 i i i 和 j j j 处的元素,其中 0 ≤ i , j < n 0 \le i, j < n 0≤i,j<n 且 j − i = 1 j - i = 1 j−i=1。由于 nums [ i ] ≠ nums [ j ] \textit{nums}[i] \ne \textit{nums}[j] nums[i]=nums[j],因此 nums [ i ] \textit{nums}[i] nums[i] 和 nums [ j ] \textit{nums}[j] nums[j] 的大小关系是 nums [ i ] > nums [ j ] \textit{nums}[i] > \textit{nums}[j] nums[i]>nums[j] 或 nums [ i ] < nums [ j ] \textit{nums}[i] < \textit{nums}[j] nums[i]<nums[j]。
当 nums [ i ] > nums [ j ] \textit{nums}[i] > \textit{nums}[j] nums[i]>nums[j] 时,下标范围 [ 0 , i ] [0, i] [0,i] 中一定存在峰值;当 nums [ i ] < nums [ j ] \textit{nums}[i] < \textit{nums}[j] nums[i]<nums[j] 时,下标范围 [ j , n − 1 ] [j, n - 1] [j,n−1] 中一定存在峰值。因此可以根据 nums [ i ] \textit{nums}[i] nums[i] 和 nums [ j ] \textit{nums}[j] nums[j] 的大小关系判断可能存在峰值的下标范围,并在更小的范围内查找峰值。
基于上述思路,可以使用二分查找的做法寻找峰值。
用 low \textit{low} low 和 high \textit{high} high 分别表示二分查找的下标范围的下界和上界,初始时 low \textit{low} low 和 high \textit{high} high 分别为数组的最小下标和最大下标。每次查找时,取 mid \textit{mid} mid 为 low \textit{low} low 和 high \textit{high} high 的平均数向下取整,比较 nums [ mid ] \textit{nums}[\textit{mid}] nums[mid] 和 nums [ mid + 1 ] \textit{nums}[\textit{mid} + 1] nums[mid+1] 的大小关系(注意当 low < high \textit{low} < \textit{high} low<high 时, mid ≤ n − 2 \textit{mid} \le n - 2 mid≤n−2,因此下标 mid + 1 \textit{mid} + 1 mid+1 不会超出数组下标范围),判断可能存在峰值的下标范围,调整查找的下标范围。
-
如果 nums [ mid ] > nums [ mid + 1 ] \textit{nums}[\textit{mid}] > \textit{nums}[\textit{mid} + 1] nums[mid]>nums[mid+1],则下标范围 [ low , mid ] [\textit{low}, \textit{mid}] [low,mid] 中可能存在峰值,在该下标范围中继续查找。
-
如果 nums [ mid ] < nums [ mid + 1 ] \textit{nums}[\textit{mid}] < \textit{nums}[\textit{mid} + 1] nums[mid]<nums[mid+1],则下标范围 [ mid + 1 , high ] [\textit{mid} + 1, \textit{high}] [mid+1,high] 中可能存在峰值,在该下标范围中继续查找。
当 low = high \textit{low} = \textit{high} low=high 时,查找结束,此时 nums [ low ] \textit{nums}[\textit{low}] nums[low] 即为峰值,返回 low \textit{low} low。
证明
为了证明上述二分查找做法的正确性,对于 0 ≤ i , j < n 0 \le i, j < n 0≤i,j<n 且 j − i = 1 j - i = 1 j−i=1,需要证明:当 nums [ i ] > nums [ j ] \textit{nums}[i] > \textit{nums}[j] nums[i]>nums[j] 时,下标范围 [ 0 , i ] [0, i] [0,i] 中一定存在峰值;当 nums [ i ] < nums [ j ] \textit{nums}[i] < \textit{nums}[j] nums[i]<nums[j] 时,下标范围 [ j , n − 1 ] [j, n - 1] [j,n−1] 中一定存在峰值。可以使用反证法证明。
当 nums [ i ] > nums [ j ] \textit{nums}[i] > \textit{nums}[j] nums[i]>nums[j] 时,假设下标范围 [ 0 , i ] [0, i] [0,i] 中不存在峰值。由于 nums [ i ] \textit{nums}[i] nums[i] 不是峰值,因此 nums [ i ] < nums [ i − 1 ] \textit{nums}[i] < \textit{nums}[i - 1] nums[i]<nums[i−1]。从下标 i − 1 i - 1 i−1 向左遍历数组 nums \textit{nums} nums,对于每个下标 k k k,由于 nums [ k + 1 ] \textit{nums}[k + 1] nums[k+1] 和 nums [ k ] \textit{nums}[k] nums[k] 都不是峰值,因此 nums [ k + 1 ] < nums [ k ] < nums [ k − 1 ] \textit{nums}[k + 1] < \textit{nums}[k] < \textit{nums}[k - 1] nums[k+1]<nums[k]<nums[k−1],即数组 nums \textit{nums} nums 在下标范围 [ 0 , i ] [0, i] [0,i] 中单调递减。由于 nums [ − 1 ] = − ∞ \textit{nums}[-1] = -\infty nums[−1]=−∞,因此必有 nums [ 0 ] > nums [ − 1 ] \textit{nums}[0] > \textit{nums}[-1] nums[0]>nums[−1],此时 nums [ 0 ] \textit{nums}[0] nums[0] 是峰值。由此可以得到以下结论。
-
如果下标范围 [ 1 , i ] [1, i] [1,i] 中存在峰值,则下标范围 [ 0 , i ] [0, i] [0,i] 中存在峰值。
-
如果下标范围 [ 1 , i ] [1, i] [1,i] 中不存在峰值,则 nums [ 0 ] \textit{nums}[0] nums[0] 是峰值,下标范围 [ 0 , i ] [0, i] [0,i] 中存在峰值。
因此,下标范围 [ 0 , i ] [0, i] [0,i] 中一定存在峰值。
当 nums [ i ] < nums [ j ] \textit{nums}[i] < \textit{nums}[j] nums[i]<nums[j] 时,利用 nums [ n ] = − ∞ \textit{nums}[n] = -\infty nums[n]=−∞,同理可知下标范围 [ j , n − 1 ] [j, n - 1] [j,n−1] 中一定存在峰值。
代码
class Solution {public int findPeakElement(int[] nums) {int low = 0, high = nums.length - 1;while (low < high) {int mid = low + (high - low) / 2;if (nums[mid] > nums[mid + 1]) {high = mid;} else {low = mid + 1;}}return low;}
}
复杂度分析
-
时间复杂度: O ( log n ) O(\log n) O(logn),其中 n n n 是数组 nums \textit{nums} nums 的长度。二分查找的次数是 O ( log n ) O(\log n) O(logn),每次查找的时间是 O ( 1 ) O(1) O(1),时间复杂度是 O ( log n ) O(\log n) O(logn)。
-
空间复杂度: O ( 1 ) O(1) O(1)。