最近这段时间偷懒了,嘿嘿,好多天没更新博客了。不行,还是要勤快一点才行。
问题描述:
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.
这道题嘛,大致的意思就是给你一个乱序数组,要你找出最长的连续的递增序列,就如所举的例子那样,返回这个递增序列的长度。此外,他的要求是你写的算法必须要是常数级的时间复杂度。
虽然这是一道hard的题,但是其实如果你能想得到,就还是很简单的。我也是瞄了一眼discuss中的一点提示,才想出来的。首先根据他的时间复杂度为O(n)的情况下,还必须要保存遍历过的值(因为你必须要与遍历过的值进行比较,看是否是在一个连续序列上的),那么肯定需要常数级的搜索,因此hashmap是首选。hashmap该怎么用呢?键不用说,肯定是对应我们遍历的数值,那么值呢?因为我们是要保存连续序列的长度,因此我们可以将值设为这个数所在的连续序列的长度,初始默认为1(即这个连续序列只有自己一个的情况)。你在往hashmap中添加元素时,就分别查找左边和右边的连续序列的长度,并将自己加进去,将自己的值设为左边序列长度加右边序列长度加1(因为这里就相当于这个点成为了左边序列和右边序列的连接点,将左边序列和右边序列连接起来了)。
当然,如果你只是更新了这个加入的点所对应的序列长度,而所在序列的边界对应的长度值没有更新,那么如果边界还要继续添加点呢?这个序列的长度就会出错(因为实际长度变了,但是长度的值却没有变)。这样就会出现错误。所以你还必须要更新左右边界对应的长度值,即如代码中所示的那样,直接将键设置为当前键减左边长度(或加右边长度),则这个键就为所在序列的左边界(或右边界),再更新它对应值,就解决啦。
下面是代码:
public int longestConsecutive(int[] nums) {
if(nums.length == 0)
return 0;
//储存遍历的值和所在连续序列的长度
HashMap<Integer, Integer> tempMap = new HashMap<>();
int result = 0;
for(int n : nums) {
if(!tempMap.containsKey(n)) {
//先将左右两边序列的长度读取下来
int left = tempMap.containsKey(n-1) ? tempMap.get(n-1) : 0;
int right = tempMap.containsKey(n+1) ? tempMap.get(n+1) : 0;
//对自己的值进行更新,即左序列长度加右序列长度加1,因为当前点是左边和右边的一个连接点
int sum = left + right + 1;
//插入点
tempMap.put(n, sum);
//更新整个map中连续序列长度的最大值
if(sum > result)
result = sum;
//接下来这两步就是对当前点所在序列的左边界和右边界对应的长度值进行更新
//即更新对外的长度值
tempMap.put(n - left, sum);
tempMap.put(n + right, sum);
}
}
return result;
}
所以,我觉得这道题,如果这样子一步一步推导下来,就还是不难的。第一个关键是要想到用哈希,第二个关键就是要想到用什么方法来保存某个连续序列的长度值,并在有点添加进来的情况下,对长度值和边界进行更新等。
谢谢大家观看我的博客。如果有不明白的地方,或者是文中有错误的地方,欢迎指出,谢谢!如果大家喜欢我的博客,也可以给我点点赞。你们的点赞就是我的动力!