Skip to content

Commit 3363edb

Browse files
author
xuzheng
committed
feat:leetcode59 findPeakElement
1 parent e47bf08 commit 3363edb

File tree

5 files changed

+231
-0
lines changed

5 files changed

+231
-0
lines changed

code/Leetcode59/FindPeakElement.java

Lines changed: 102 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,102 @@
1+
package com.dixon.leetcode59;
2+
3+
public class FindPeakElement {
4+
5+
/**
6+
* 寻找峰值元素
7+
* <p>
8+
* 遍历 n
9+
*/
10+
public static int findPeakElementByTraverse(int[] nums) {
11+
// 后续这块判断就不加了..这里示范下
12+
if (nums == null || nums.length == 0) {
13+
throw new IllegalArgumentException("违规数据");
14+
}
15+
if (nums.length == 1) {
16+
return nums[0];
17+
}
18+
// 峰值临时值
19+
int peakElement = nums[0];
20+
for (int i = 1; i < nums.length; i++) {
21+
// 不存在相等的情况
22+
// 右数比左数小 则左数即为峰值元素
23+
if (nums[i] < peakElement) {
24+
return i - 1;
25+
} else {
26+
// 右数比左数大 改变峰值临时值 继续寻找
27+
peakElement = nums[i];
28+
// 遍历完了 没找到
29+
if (i == nums.length - 1) {
30+
return i;
31+
}
32+
}
33+
}
34+
return -1;
35+
}
36+
37+
/**
38+
* 寻找峰值元素
39+
* <p>
40+
* 遍历 n
41+
*/
42+
public static int findPeakElementByTraverseOptimization(int[] nums) {
43+
for (int i = 1; i < nums.length; ++i) {
44+
if (nums[i] < nums[i - 1]) return i - 1;
45+
}
46+
return nums.length - 1;
47+
}
48+
49+
/**
50+
* 寻找峰值元素
51+
* <p>
52+
* 二分法 logN
53+
*/
54+
public static int findPeakElementByDivide(int[] nums) {
55+
int start = 0;
56+
int end = nums.length - 1;
57+
int middle = 0;
58+
while (start < end) {
59+
middle = (start + end) / 2;
60+
// 峰值在右边
61+
if (nums[middle] < nums[middle + 1]) {
62+
start = middle + 1;
63+
} else {
64+
// 峰值在左边
65+
end = middle;
66+
}
67+
}
68+
// 判断是否满足终止条件:
69+
// start == end 二分到了最后一位 注意 此时middle可能没变
70+
return start;
71+
}
72+
73+
/**
74+
* 寻找峰值元素
75+
* <p>
76+
* 二分法 logN 优化
77+
*/
78+
public static int findPeakElementByDivideOptimization(int[] nums) {
79+
int start = 0;
80+
int end = nums.length - 1;
81+
int middle = 0;
82+
while (start < end) {
83+
middle = (start + end) / 2;
84+
// 峰值在右边
85+
if (nums[middle] < nums[middle + 1]) {
86+
start = middle + 1;
87+
} else {
88+
// 当前值比右值大 峰值在左边
89+
// 优化 因为找到一个即可 所以没必要二分到底
90+
if (middle != 0 && nums[middle] > nums[middle - 1]) {
91+
return middle;
92+
}
93+
end = middle;
94+
}
95+
}
96+
// 判断是否满足终止条件:
97+
// start == end 二分到了最后一位
98+
return start;
99+
}
100+
101+
// 这是迭代二分查找 另外还有递归二分查找 只不过空间复杂度更高 没什么区别
102+
}

doc/Leetcode59/Leetcode59.md

Lines changed: 129 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,129 @@
1+
# 寻找峰值
2+
3+
## 题目要求
4+
5+
峰值元素是指其值大于左右相邻值的元素。
6+
7+
给定一个输入数组 nums,其中 nums[i] ≠ nums[i+1],找到峰值元素并返回其索引。
8+
9+
数组可能包含多个峰值,在这种情况下,返回任何一个峰值所在位置即可。
10+
11+
你可以假设 nums[-1] = nums[n] = -∞。
12+
13+
示例 1:
14+
15+
```java
16+
输入: nums = [1,2,3,1]
17+
输出: 2
18+
解释: 3 是峰值元素,你的函数应该返回其索引 2
19+
```
20+
21+
示例 2:
22+
23+
```java
24+
输入: nums = [1,2,1,3,5,6,4]
25+
输出: 15
26+
解释: 你的函数可以返回索引 1,其峰值元素为 2
27+
  或者返回索引 5, 其峰值元素为 6
28+
```
29+
30+
示例 3:
31+
32+
```java
33+
输入: nums = [1,2,3]
34+
输出: 2
35+
解释: 3 是峰值元素,你的函数应该返回其索引 2
36+
```
37+
38+
示例 4:
39+
40+
```java
41+
输入: nums = [3,2,1]
42+
输出: 0
43+
解释: 3 是峰值元素,你的函数应该返回其索引 0
44+
```
45+
46+
要求:时间复杂度 O(logN)
47+
48+
## 解法
49+
50+
### 线性扫描
51+
52+
#### 思路
53+
54+
一句话,出现降序,即为峰值。
55+
56+
下面一一列举。
57+
58+
场景1:纯降序数组。(0点出现降序,所以为峰值)
59+
60+
![纯降序](../../res/leetcode59/纯降序.jpg)
61+
62+
场景2:纯升序数组。(末尾出现降序,所以为峰值)
63+
64+
![纯升序](../../res/leetcode59/纯升序.jpg)
65+
66+
场景3:升降复合数组。(n点出现降序,即为峰值)
67+
68+
![升降混合](../../res/leetcode59/升降混合.jpg)
69+
70+
71+
#### 代码
72+
73+
```java
74+
/**
75+
* 寻找峰值元素
76+
* <p>
77+
* 遍历 n
78+
*/
79+
public static int findPeakElement(int[] nums) {
80+
for (int i = 1; i < nums.length; ++i) {
81+
if (nums[i] < nums[i - 1]) return i - 1;
82+
}
83+
return nums.length - 1;
84+
}
85+
```
86+
87+
很明显,不能满足 O(logN) 的要求。
88+
89+
### 二分查找
90+
91+
#### 思路
92+
93+
寻找中点元素。
94+
如果中点元素的值小于右边元素的值,则说明峰值在右边,抛弃左边和中点的数;
95+
如果中点元素的值大于右边元素的值,则说明峰值在左边,抛弃右边的数。
96+
97+
见演示 Demo。
98+
99+
#### 代码
100+
101+
```java
102+
/**
103+
* 寻找峰值元素
104+
* <p>
105+
* 二分法 logN
106+
*/
107+
public static int findPeakElementByDivide(int[] nums) {
108+
int start = 0;
109+
int mid = 0;
110+
int end = nums.length - 1;
111+
while (start < end) {
112+
mid = (start + end) / 2;
113+
if (nums[mid] < nums[mid + 1]) {// 峰值在右边
114+
start = mid + 1;
115+
} else {// 峰值在左边
116+
end = mid;
117+
}
118+
}
119+
// start == end 二分到了最后一位 注意 此时mid可能没变
120+
return start;
121+
}
122+
```
123+
124+
时间复杂度满足题目要求。
125+
126+
> 耗时就不单独算了,偶然性太大,除非是最坏情况,但是最坏情况实际就是 `O(N)``O(log2N)` 的耗时对比了,没有太大的意义。
127+
128+
> 另外二分查找还有递归形式,没什么太大区别,而且空间复杂度更高(也是 logN)。
129+

res/Leetcode59/升降混合.jpg

39.1 KB
Loading

res/Leetcode59/纯升序.jpg

37.9 KB
Loading

res/Leetcode59/纯降序.jpg

37.8 KB
Loading

0 commit comments

Comments
 (0)