峰值元素是指其值严格大于左右相邻值的元素。
给你一个整数数组 nums
,找到峰值元素并返回其索引。数组可能包含多个峰值,在这种情况下,返回 任何一个峰值 所在位置即可。
你可以假设 nums[-1] = nums[n] = -∞
。
你必须实现时间复杂度为 O(log n)
的算法来解决此问题。
目录
常见排序算法的时间复杂度
1. 线性查找(顺序查找)
- 时间复杂度: O(n)
- 描述: 线性查找通过遍历数组或列表中的每个元素,逐一检查是否与目标值匹配。它不需要数据预先排序。
2. 二分查找
- 时间复杂度: O(log n)
- 描述: 二分查找适用于已排序的数组,每次将查找范围缩小一半,直到找到目标值或查找范围为空。
3. 二叉搜索树查找
- 时间复杂度:
- 平均情况: O(log n)
- 最坏情况: O(n)(当树退化为链表时)
- 描述: 在二叉搜索树中查找元素,每一步可以排除一半的可能性,但如果树不平衡,效率会下降。
4. 平衡二叉搜索树查找(如AVL树、红黑树)
- 时间复杂度: O(log n)
- 描述: 平衡二叉搜索树通过维持树的平衡,确保查找、插入和删除操作的最坏情况时间复杂度都是 O(log n)。
5. 哈希查找
- 时间复杂度:
- 平均情况: O(1)
- 最坏情况: O(n)(当哈希冲突导致很多元素映射到同一个哈希桶时)
- 描述: 通过哈希表进行查找,元素通过哈希函数映射到一个桶中。理想情况下可以直接定位到元素,但需要处理哈希冲突。
6. 跳表查找
- 时间复杂度: O(log n)
- 描述: 跳表是对链表的一种改进,通过增加多级索引来提高查找效率。适用于元素有序的情况。
7. 斐波那契查找
- 时间复杂度: O(log n)
- 描述: 斐波那契查找是二分查找的一种变体,使用斐波那契数列来分割数组,适用于已排序的数组。
解题关键及代码
根据时间复杂度的提示,可以考虑二分查找的方法,本题的解题重点在于:在题目描述中出现了 nums[-1] = nums[n] = -∞,这就代表着 只要数组中存在一个元素比相邻元素大,那么沿着它一定可以找到一个峰值。
class Solution {
public int findPeakElement(int[] nums) {
int left = 0, right = nums.length - 1;
for (; left < right; ) {
int mid = left + (right - left) //注意点1:整数溢出
if (nums[mid] > nums[mid + 1]) {
right = mid;
} else {
left = mid + 1;//注意点2:注意边界的修改,防止无限循环超时
}
}
return left;
}
}
二分法的一些关键点
1.整数溢出
取中点时:left + (right - left) / 2,这是因为
在计算机中,整数的存储是有限的。例如,在Java中,int
类型是32位的,能够表示的数值范围是从 -2^31
到 2^31 - 1
(即-2,147,483,648到2,147,483,647)。如果进行的计算超出了这个范围,就会发生溢出,意味着计算结果会不正确地“回绕”到整数的另一端。
直接计算中点可能导致的溢出
当你直接使用 (left + right) / 2
来计算中点时,如果 left
和 right
都非常大(接近int
类型的最大值),它们的和可能会超出 int
能够表示的最大值,导致溢出。这种情况下,计算的中点值将是不正确的。
使用 left + (right - left) / 2
避免溢出
这个方法通过首先计算 right
和 left
的差值,然后将这个差值除以2,最后将结果加到 left
上,来计算中点。这种方式的关键在于:
right - left
不会超过int
类型的最大值(因为即使在最极端的情况下,left
是0,right
是2^31 - 1
,它们的差值也是在int
的表示范围内)。- 由于
left
和(right - left) / 2
的和不会超过int
的最大值,这种方式避免了溢出的风险。
2.边界问题(第一次做right边界没改超时了555)
-
更新
right
: 如果nums[mid]
大于nums[mid + 1]
,这表明目标元素可能在当前中点的左侧(包括中点自身),因为你已经找到了一个递减的序列。因此,你将right
更新为mid
,来缩小搜索区间到左半部分。
-
更新
left
: 如果nums[mid]
不大于nums[mid + 1]
(即else
分支),这意味着目标元素位于中点的右侧,因为你处于一个递增的序列。这时,你需要将left
更新为mid + 1
,排除中点和它左侧的所有元素,将搜索范围缩小到右半部分。这里mid + 1
是因为你已经确定mid
不是目标,所以从mid
的下一个元素开始搜索。