162J. Find Peak Element
https://leetcode.com/problems/find-peak-element/
峰值这个题最关键的一点就是证明 binary search 在这里是可行的。
由于 nums[-1] = nums[nums.length] = - \infity
我们只要发现 nums[i] < nums[i + 1] 就有 峰值在 i 的右侧
只要发现 nums[i] > nums[i + 1] 就有峰值在 i + 1 左侧
Method 普通
这是完全普通按照定义来的, 由于只需要返回任意一个峰值,所以很简单。
class Solution {
public int findPeakElement(int[] nums) {
if(nums.length < 2){
return 0;
}
List<Integer> list = new ArrayList<>();
for(int i = 1; i < nums.length - 1; i++){
if(nums[i] > nums[i-1] && nums[i] > nums[i+1]){
return i;
}
}
return nums[0] > nums[1] ? 0:nums.length - 1;
}
}
Method Best: 二分法查找
下面这个为什么不行? 因为取 pivot - 1, pivot + 1。edge case 会太多。
class Solution {
public int findPeakElement(int[] nums) {
if(nums.length < 2){
return 0;
}
int left = 0;
int right = nums.length - 1;
int pivot = 0;
while(left <= right){
pivot = left + (right - left)/ 2;
if( nums[pivot] > nums[pivot - 1] && nums[pivot] > nums[pivot + 1]){
return pivot;
}else if( nums[pivot] > nums[pivot - 1] && nums[pivot] < nums[pivot + 1] ){
left = pivot + 1;
}else{
right = pivot - 1;
}
}
return left;
}
}
可行的代码如下
class Solution {
public int findPeakElement(int[] nums) {
if(nums.length < 2){
return 0;
}
int left = 0;
int right = nums.length - 1;
int pivot = 0;
while(left < right){
pivot = left + (right - left)/ 2;
if(nums[pivot] > nums[pivot + 1]){
// 如果 pivot 比 pivot + 1 大,说明左侧有极大
// 分割区域,包含 pivot
right = pivot;
}else{
// 反之说明右侧有极大,
// 分割区域,不包含 pivot.
left = pivot + 1;
}
}
//用 left < right作为判据 最终 left = right
// 这里由于是找极大,和一般的找特定值不同。
return right;
}
}
同样的思想,也可以这么写
class Solution {
public int findPeakElement(int[] nums) {
if(nums.length < 2){
return 0;
}
int left = 0;
int right = nums.length - 1;
int pivot = 0;
while(left < right){
pivot = left + (right - left)/ 2;
if(nums[pivot] < nums[pivot + 1]){
left = pivot + 1;
}else{
right = pivot;
}
}
return right;
}
}
看看 2022 最新总结的思想:
class Solution {
public int findPeakElement(int[] nums) {
int lo = 0;
int hi = nums.length - 1;
while(lo <= hi){
int mid = (hi + lo) >>> 1;
boolean whatWeWantToBeTrue = mid + 1 == nums.length ? false : nums[mid] < nums[mid + 1];
if(whatWeWantToBeTrue){
lo = mid + 1;
}else{
hi = mid - 1;
}
}
return lo;
}
}
2023
基于上述的总结:
峰值这个题最关键的一点就是证明 binary search 在这里是可行的。 由于 nums[-1] = nums[nums.length] = - \infity 我们只要发现 nums[i] < nums[i + 1] 就有 峰值在 i 的右侧 只要发现 nums[i] > nums[i + 1] 就有峰值在 i + 1 左侧
既可以 令 nums[mid] < nums[mid + 1] 然后 left = mid + 1
class Solution {
public int findPeakElement(int[] nums) {
int left = 0;
int right = nums.length - 1;
while (left <= right) {
int mid = (left + right) >>> 1;
// [T,T,T,T,F,F,F,F], T 是 上升。找到的是第一个 F, return left
boolean whatWeWantToBeTrue = mid == nums.length - 1 ? false : nums[mid] < nums[mid + 1];
if (whatWeWantToBeTrue) {
left = mid + 1;
} else {
right = mid - 1;
}
}
return left;
}
}
也可以 令 nums[mid - 1] > nums[mid] 然后 right = mid - 1
class Solution {
public int findPeakElement(int[] nums) {
int left = 0;
int right = nums.length - 1;
while (left <= right) {
int mid = (left + right) >>> 1;
// [F,F,F,F,T,T,T,T], T 是 下降。找到的是第一个 T 之前的 F, return right
boolean whatWeWantToBeTrue = mid == 0 ? false : nums[mid - 1] > nums[mid];
if (whatWeWantToBeTrue) {
right = mid - 1;
} else {
left = mid + 1;
}
}
return right;
}
}
你看上面两个写法都是对的。
Last updated
Was this helpful?