Skip to content

Commit e7cbca2

Browse files
committed
128
1 parent e4822f9 commit e7cbca2

File tree

3 files changed

+140
-3
lines changed

3 files changed

+140
-3
lines changed

SUMMARY.md

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -102,7 +102,7 @@
102102
* [99. Recover Binary Search Tree](leetcode-99-Recover-Binary-Search-Tree.md)
103103
* [100. Same Tree](leetcode-100-Same-Tree.md)
104104
* [leetcode 100 斩!回顾](leetcode100斩回顾.md)
105-
* [101 题到 127](leetcode-101-200.md)
105+
* [101 题到 128](leetcode-101-200.md)
106106
* [101. Symmetric Tree](leetcode-101-Symmetric-Tree.md)
107107
* [102. Binary Tree Level Order Traversal](leetcode-102-Binary-Tree-Level-Order-Traversal.md)
108108
* [103. Binary Tree Zigzag Level Order Traversal](leetcode-103-Binary-Tree-Zigzag-Level-Order-Traversal.md)
@@ -129,4 +129,5 @@
129129
* [124*. Binary Tree Maximum Path Sum](leetcode-124-Binary-Tree-Maximum-Path-Sum.md)
130130
* [125. Valid Palindrome](leetcode-125-Valid-Palindrome.md)
131131
* [126*. Word Ladder II](leetCode-126-Word-LadderII.md)
132-
* [127. Word Ladder](leetCode-127-Word-Ladder.md)
132+
* [127. Word Ladder](leetCode-127-Word-Ladder.md)
133+
* [128. Longest Consecutive Sequence](leetcode-128-Longest-Consecutive-Sequence.md)

leetcode-101-200.md

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -50,4 +50,6 @@
5050

5151
<a href="leetCode-126-Word-LadderII.html">126. Word Ladder II</a>
5252

53-
<a href="leetCode-127-Word-Ladder.html">127. Word Ladder</a>
53+
<a href="leetCode-127-Word-Ladder.html">127. Word Ladder</a>
54+
55+
<a href="leetcode-128-Longest-Consecutive-Sequence.html">128. Longest Consecutive Sequence</a>
Lines changed: 134 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,134 @@
1+
# 题目描述(困难难度)
2+
3+
![](https://windliang.oss-cn-beijing.aliyuncs.com/128.jpg)
4+
5+
给一个数组,求出连续的数字最多有多少个,时间复杂度要求是 `O(n)`
6+
7+
# 解法一
8+
9+
首先想一下最直接的暴力破解。我们可以用一个 `HashSet` 把给的数组保存起来。然后再考虑数组的每个数,比如这个数是 `n`,然后看 `n + 1` 在不在 `HashSet` 中,然后再看 `n + 2` 在不在,接下来 `n + 3``n + 4` 直到在 `HashSet` 中找不到,记录当前的长度。然后继续考虑下一个数,并且更新最长的长度。
10+
11+
```java
12+
public int longestConsecutive(int[] nums) {
13+
HashSet<Integer> set = new HashSet<>();
14+
for (int i = 0; i < nums.length; i++) {
15+
set.add(nums[i]);
16+
}
17+
int max = 0;
18+
for (int i = 0; i < nums.length; i++) {
19+
int num = nums[i];
20+
int count = 0;
21+
while (set.contains(num)) {
22+
count++;
23+
num += 1;
24+
}
25+
max = Math.max(max, count);
26+
}
27+
return max;
28+
}
29+
```
30+
31+
当然时间复杂度不符合题意了,我们想一下优化方案。
32+
33+
上边的暴力破解有一个问题就是做了很多没必要的计算,因为我们要找最长的连续数字。所以如果是数组 `54367`,当我们遇到 `5` 的时候计算一遍 `567`。遇到 `4` 又计算一遍 `4567`。遇到 `3` 又计算一遍 `34567`。很明显从 `3` 开始才是我们想要的序列。
34+
35+
换句话讲,我们只考虑从序列最小的数开始即可。实现的话,当考虑 `n` 的时候,我们先看一看 `n - 1` 是否存在,如果不存在,那么从 `n` 开始就是我们需要考虑的序列了。否则的话,直接跳过。
36+
37+
```java
38+
public int longestConsecutive(int[] nums) {
39+
HashSet<Integer> set = new HashSet<>();
40+
for (int i = 0; i < nums.length; i++) {
41+
set.add(nums[i]);
42+
}
43+
int max = 0;
44+
for (int i = 0; i < nums.length; i++) {
45+
int num = nums[i];
46+
//n - 1 是否存在
47+
if (!set.contains(num - 1)) {
48+
int count = 0;
49+
while (set.contains(num)) {
50+
count++;
51+
num += 1;
52+
}
53+
max = Math.max(max, count);
54+
}
55+
}
56+
return max;
57+
}
58+
```
59+
60+
这个时间复杂度的话就是 `O(n)` 了。虽然 `for` 循环里套了 `while` 循环,但每个元素其实最多也就是被访问两次。比如极端情况 `987654``98765` 循环的时候都不会进入 `while` 循环,只有到 `4` 的时候才进入了 `while` 循环。所以总共的话, `98765` 也只会被访问两次,所以时间复杂度就是 `O(n)` 了。
61+
62+
# 解法二
63+
64+
参考 [这里](<https://leetcode.com/problems/longest-consecutive-sequence/discuss/41055/My-really-simple-Java-O(n)-solution-Accepted>) ,虽然不容易直接想到,但还是有迹可循的。
65+
66+
本质上就是把连续的序列进行合并,思路就是考虑我们先解决了小问题,然后大问题怎么解决。
67+
68+
```java
69+
假如我们已经了有连续的序列,12356,并且序列的边界保存了当前序列的长度。
70+
1 2 3
71+
3 3 <- 序列长度
72+
73+
5 6
74+
2 2 <- 序列长度
75+
76+
此时来了一个数字 4
77+
我们只需要考虑 4 - 1 = 3,以 3 结尾的序列的长度
78+
以及 4 + 1 = 5,以 5 开头的序列的长度
79+
所以当前就会得到一个包含 4 的,长度为 3 + 1 + 2 = 6 的序列
80+
1 2 3 4 5 6
81+
3 3 2 2 <- 序列长度
82+
83+
此时把两个边界的长度进行更新
84+
1 2 3 4 5 6
85+
6 3 2 6 <- 序列长度
86+
87+
此时如果又来了 7
88+
我们只需要考虑 7 - 1 = 6,以 6 结尾的序列的长度
89+
以及 7 + 1 = 8,以 8 开头的序列的长度,但是不存在以 8 开头的序列,所以这个长度是 0
90+
所以当前就会得到一个包含 7 的,长度为 6 + 1 + 0 = 7 的序列
91+
1 2 3 4 5 6 7
92+
6 3 2 6 <- 序列长度
93+
94+
此时把两个边界的长度进行更新
95+
1 2 3 4 5 6 7
96+
7 3 2 6 7 <- 序列长度
97+
```
98+
99+
实现的话,我们可以用一个 `HashMap` ,存储以当前 `key` 为边界的连续序列的长度。可以再结合代码理解一下。
100+
101+
```java
102+
public int longestConsecutive(int[] nums) {
103+
HashMap<Integer, Integer> map = new HashMap<>();
104+
int max = 0;
105+
for (int i = 0; i < nums.length; i++) {
106+
int num = nums[i];
107+
//已经考虑过的数字就跳过,必须跳过,不然会出错
108+
//比如 [1 2 1]
109+
//最后的 1 如果不跳过,因为之前的 2 的最长长度已经更新成 2 了,所以会出错
110+
if(map.containsKey(num)) {
111+
continue;
112+
}
113+
//找到以左边数字结尾的最长序列,默认为 0
114+
int left = map.getOrDefault(num - 1, 0);
115+
//找到以右边数开头的最长序列,默认为 0
116+
int right = map.getOrDefault(num + 1, 0);
117+
int sum = left + 1 + right;
118+
max = Math.max(max, sum);
119+
120+
//将当前数字放到 map 中,防止重复考虑数字,value 可以随便给一个值
121+
map.put(num, -1);
122+
//更新左边界长度
123+
map.put(num - left, sum);
124+
//更新右边界长度
125+
map.put(num + right, sum);
126+
}
127+
return max;
128+
}
129+
```
130+
131+
#
132+
133+
两种思路其实都是正常的操作,仔细想的话还是可以想出来的。
134+

0 commit comments

Comments
 (0)