问题描述:
Given an unsorted array of integers, find the length of the longest consecutive elements sequence.
For example,
Given [100, 4, 200, 1, 3, 2],
The longest consecutive elements sequence is [1, 2, 3, 4]. Return its length: 4.
Your algorithm should run in O(n) complexity.
我首先想到的是一个O(n2)的方法:就是遍历每个元素,查看该元素是否与前面的set中相邻(+-为1),如果是,将其加入到该set中,如果不是,新建一个set。
然后我又想,这里面大量的时间实际上是花在了遍历set中,如果将set变成一个hash表,按查找时间就将为了O(1),此时时间复杂度大概为O(nlogn)。这个想法,比较糟糕的地方是虽然查找时间降为了O(1),但是需要不断的将新元素加入到对应的key set中,这样很不方便实现,而且需要递归的循环下去。
但是在网上看到了其他人的想法,才觉得转换一个思路真的太奇妙了。同样是利用hash表。首先对序列做一个预处理,即将序列中元素添加到hashset中,然后再做一次遍历,如果某元素在hashset中,那么查找它的前一个元素,如果存在,就删除该元素。然后计数加1;
代码如下:
public int longestConsecutive(int[] nums) {
HashSet<Integer> sets = new HashSet<Integer>();
//prepare for process
for(int i = 0;i<nums.length;i++){
sets.add(nums[i]);
}
int tmp;
int value,realValue = 1;
for(int j = 0;j<nums.length;j++){
tmp = nums[j];
value = 1;
if(!sets.contains(tmp))//deleted already
continue;
while(true){
if(sets.contains(tmp-1)){
value++;
sets.remove(tmp-1);
tmp = tmp-1;
}
else
break;
}
tmp = nums[j];
while(true){
if(sets.contains(tmp+1)){
value++;
sets.remove(tmp+1);
tmp = tmp+1;
}else
break;
}
realValue = (realValue>value)?realValue:value;
}
return realValue;
}
First turn the input into a set of numbers. That takes O(n) and then we can ask in O(1) whether we have a certain number.
Then go through the numbers. If the number x is the start of a streak (i.e., x-1 is not in the set), then test y = x+1, x+2, x+3, ...
and stop at the first number y not in the set. The length of the streak is then simply y-x and we update our global best with that.
Since we check each streak only once, this is overall O(n). This ran in 44 ms on the OJ
public class Solution {
public int longestConsecutive(int[] nums) {
Set<Integer> set = new HashSet<>();
for(int n : nums) {
set.add(n);
}
int best = 0;
for(int n : set) {
if(!set.contains(n - 1)) { // only check for one direction
int m = n + 1;
while(set.contains(m)) {
m++;
}
best = Math.max(best, m - n);
}
}
return best;
}
}
We will use HashMap. The key thing is to keep track of the sequence length and store that in the boundary points of the sequence.
For example, as a result, for sequence {1, 2, 3, 4, 5}, map.get(1) and map.get(5) should both return 5.
Whenever a new element n is inserted into the map, do two things:
1、See if n - 1 and n + 1 exist in the map, and if so, it means there is an existing sequence next to n.
Variables left and right will be the length of those two sequences, while 0 means there is no sequence and n will be
the boundary point later. Store (left + right + 1) as the associated value to key n into the map.
2、Use left and right to locate the other end of the sequences to the left and right of n respectively, and replace the value with the
new length.
Everything inside the for loop is O(1) so the total time is O(n). Please comment if you see something wrong. Thanks.
public int longestConsecutive(int[] num) {
int res = 0;
HashMap<Integer, Integer> map = new HashMap<Integer, Integer>();
for (int n : num) {
if (!map.containsKey(n)) {
int left = (map.containsKey(n - 1)) ? map.get(n - 1) : 0;
int right = (map.containsKey(n + 1)) ? map.get(n + 1) : 0;
int sum = left + right + 1;
map.put(n, sum);
res = Math.max(res, sum);
map.put(n - left, sum);
map.put(n + right, sum);
}
else {
continue;
}
}
return res;
}