题目描述
原题链接: LeetCode.162 寻找峰值
解题思路
- 数组相邻元素不相等, 峰值可能有多个, 整个数组的最大值肯定是峰值之一, 直接遍历数组获取最大值也能得到答案;
- 但是写明复杂度要求O(log n)就是否定了最简单的O(n)遍历解法, 需要用二分法;
- 按照题意数组边界另一端等同于无穷小, 可以直接分别按照首尾两个数字大小来判断首尾是否是峰值;
- 如果首尾不是峰值, 说明在首位数组值是上升, 末尾数组是下降的趋势, 必然在中间某个位置至少存在一个峰值将元素趋势从上升变为下降;
- 考虑连续三个元素的大小关系来判断峰值:
- nums[i - 1] < nums[i] > nums[i + 1], i就是峰值;
- nums[i - 1] < nums[i] < nums[i + 1], i位置仍然处于上升趋势, 要在大于i的右边区域继续查找;
- nums[i - 1] > nums[i] > nums[i + 1], i位置处于下降趋势, 在小于i的左边区域可以找到峰值;
- nums[i - 1] > nums[i] < nums[i + 1], i是谷底值, 左右两边区域都存在峰值;
- 总结4种情况, nums[i] > nums[i + 1]时峰值可能是i本身或者要继续在[0, i-1]范围内找, nums[i] < nums[i + 1]时峰值都可以在[i + 1, n - 1]范围找到, 这就确定了二分逻辑;
解题代码
-
个人倾向于使用的便于理解的写法:
/** * 朴素好读懂的二分解法 * 执行用时: 0 ms , 在所有 Java 提交中击败了 100.00% 的用户 * 内存消耗: 41.24 MB , 在所有 Java 提交中击败了 83.37% 的用户 * 编辑日期: 2024-05-10 */ public int findPeakElement(int[] nums) { if (nums.length == 1 || nums[1] < nums[0]) { return 0; } int right = nums.length - 1; if (nums[right - 1] < nums[right]) { return right; } int left = 0, ans = 0; while (left <= right) { int mid = left + ((right - left) >> 1); if (nums[mid] > nums[mid + 1]) { right = mid - 1; ans = mid; } else { left = mid + 1; } } return ans; }
-
更简洁的写法, 不熟悉的人看到可能要举几个例子才好理解:
/** * 更简洁代码版本, 去除了边界判断和辅助变量ans * 执行用时: 0 ms , 在所有 Java 提交中击败了 100.00% 的用户 * 内存消耗: 40.04 MB , 在所有 Java 提交中击败了 100.00% 的用户 */ public int findPeakElement(int[] nums) { int left = 0, right = nums.length - 1; while (left < right) { int mid = left + ((right - left) >> 1); if (nums[mid] < nums[mid + 1]) { left = mid + 1; } else { right = mid; } } return left; }