题目来源:https://leetcode-cn.com/problems/find-peak-element/
大致题意:
给出一个数组,返回峰值元素的下标。峰值元素就是极大值,其大于左边的元素和右边的元素。峰值元素可能有多个,返回任意一个即可
思路
如果不是题目要求算法的时间复杂度为 O(logn),那直接遍历一遍就完事了
考虑到当前元素与左右元素的大小,有四种情况
- 当前元素 小于 左边元素,也 小于 右边元素,当前处于坡底,为极小值,这时候要找坡顶往左往右差别不大
- 当前元素 小于 左边元素,但 大于 右边元素,当前处于下坡状态,要找坡顶需要往左边
- 当前元素 大于 左边元素,也 大于 右边元素,当前处于坡顶,为极大值,直接返回
- 当前元素 大于 左边元素,但 小于 右边元素,当前处于上坡状态,要找坡顶需要往右边
于是按照这个方法找坡顶就行,但是这也不是 O(logn) 的算法
二分
在刚刚的四种情况中,可以发现如果出现 2,那么接下来该位置右边的部分就不会再遍历到了(因为想到右边需要从当前位置查过去,但是只要到当前位置就一定是往左走);同样,如果出现 4,那么左边的部分就不会遍历到。
那如果我们再令出现情况 1 时,固定朝一个方向走,如固定朝右走,这就是二分了
代码:
public int findPeakElement(int[] nums) {
int ans = binartSearch(nums);
return ans;
}
public int binartSearch(int[] nums) {
int left = 0;
int right = nums.length - 1;
while (left <= right) {
int mid = (left + right) / 2;
// 处于峰值
if (compare(nums, mid, mid - 1) > 0 && compare(nums, mid, mid + 1) > 0) {
return mid;
}
// 若小于右边元素,往右半部分查
if (compare(nums, mid, mid + 1) < 0) {
left = mid + 1;
// 大于右边元素,往左半部分查
} else {
right = mid - 1;
}
}
return left;
}
// 比较两个索引处的值大小
public int compare(int[] nums, int idx1, int idx2) {
// 索引为 -1 和 n 处,都当无限小处理
if (idx1 == -1) {
return -1;
} else if (idx2 == nums.length) {
return 1;
}
return nums[idx1] > nums[idx2] ? 1 : -1;
}