Idiot-maker

  :: 首页 :: 博问 :: 闪存 :: 新随笔 :: 联系 :: 订阅 订阅 :: 管理 ::

https://leetcode.com/problems/longest-consecutive-sequence/

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(n)的时间。原来的数组没有排序,如果先排序,必然要花费O(nlogn)的时间,不符合要求。根据经验和直觉,可能是用O(n)的时间去遍历数组,然后借助HashMap,花费O(1)的时间去找某些元素在不在数组中。

按照这样的思路往下想,对于num[i]去寻找它的左侧和右侧邻接的元素,直到在num中不存在,这时更新长度。因为每个数字只能存在于唯一的一个consecutive sequence里,否则这两个sequence就是相连的。

所以,这里拓展过的元素就完全可以从set中删掉了。后面再遍历到的num[j]就可以直接跳过,因为它属于的最长consecutive sequence前面已经找出了。

这样,整体的时间复杂度一定是O(n)。

下面的代码使用了HashMap,同时对num中出现的数字计数。

public class Solution {
    public int longestConsecutive(int[] num) {
        Map<Integer, Integer> numMap = new HashMap<Integer, Integer>();
        for(int i = 0; i < num.length; i++){
            if(numMap.containsKey(num[i])){
                numMap.put(num[i], numMap.get(num[i]) + 1);
            }else{
                numMap.put(num[i], 1);
            }
        }
        
        int longestLength = 0;
        
        for(int i = 0; i < num.length; i++){
            int currentLength = 0;
            int next = num[i];
            while(numMap.containsKey(next) && numMap.get(next) > 0){
                numMap.put(next, numMap.get(next) - 1);
                currentLength++;
                next++;
            }
            
            int previous = num[i] - 1;
            while(numMap.containsKey(previous) && numMap.get(previous) > 0){
                numMap.put(previous, numMap.get(previous) - 1);
                currentLength++;
                previous--;
            }
            longestLength = Math.max(longestLength, currentLength);
        }
        
        return longestLength;
    }
}

后来发现根本没必要,因为一个数字即使出现多次,还是只能出现在一个consecutive sequence里,换成set。

public class Solution {
    public int longestConsecutive(int[] num) {
        Set<Integer> numSet = new HashSet<Integer>();
        for(int i = 0; i < num.length; i++){
            if(!numSet.contains(num[i])){
                numSet.add(num[i]);
            }
        }
        
        int longestLength = 0;
        
        for(int i = 0; i < num.length; i++){
            int currentLength = 0;
            int next = num[i];
            while(numSet.contains(next)){
                numSet.remove(next);
                currentLength++;
                next++;
            }
            
            int previous = num[i] - 1;
            while(numSet.contains(previous)){
                numSet.remove(previous);
                currentLength++;
                previous--;
            }
            longestLength = Math.max(longestLength, currentLength);
        }
        
        return longestLength;
    }
}

再优化,进入循环前就直接判断num[i]在不在set里,不在直接跳过继续!

public class Solution {
    public int longestConsecutive(int[] num) {
        Set<Integer> numSet = new HashSet<Integer>();
        for(int i = 0; i < num.length; i++){
            numSet.add(num[i]);
        }
        
        int longestLength = 0;
        
        for(int i = 0; i < num.length; i++){
            if(!numSet.contains(num[i])){
                continue;
            }
            int currentLength = 0;
            int next = num[i];
            while(numSet.contains(next)){
                numSet.remove(next);
                currentLength++;
                next++;
            }
            
            int previous = num[i] - 1;
            while(numSet.contains(previous)){
                numSet.remove(previous);
                currentLength++;
                previous--;
            }
            longestLength = Math.max(longestLength, currentLength);
        }
        return longestLength;
    }
}

特别警醒的是,开始我的for改成这样,这样如果num[i]不在set里循环就直接退出了,而不是continue。犯了如此基本的错误!

for(int i = 0; i < num.length && numSet.contains(num[i]); i++){
            int currentLength = 0;
            int next = num[i];
            while(numSet.contains(next)){
                numSet.remove(next);
                currentLength++;
                next++;
            }
            
            int previous = num[i] - 1;
            while(numSet.contains(previous)){
                numSet.remove(previous);
                currentLength++;
                previous--;
            }
            longestLength = Math.max(longestLength, currentLength);
        }

 这就是我的思路。从O(nlogn)到O(n)一般都是这么几个方法。这里使用了空间换时间的思路。

//20181123

大神的解法

https://leetcode.com/problems/longest-consecutive-sequence/discuss/41055/My-really-simple-Java-O(n)-solution-Accepted

class Solution {
    public int longestConsecutive(int[] nums) {
        HashMap<Integer, Integer> num2length = new HashMap<Integer, Integer>();
        int res = 0;
        
        for (int num : nums) {
            if (num2length.containsKey(num)) {
                continue;
            }
            
            int left = num2length.getOrDefault(num - 1, 0);
            int right = num2length.getOrDefault(num + 1, 0);
            int length = left + right + 1;
            res = Math.max(res, length);
            
            num2length.put(num, length);
            num2length.put(num - left, length);
            num2length.put(num + right, length);          
        }
        return res;
    }
}

 

posted on 2015-03-19 20:20  NickyYe  阅读(154)  评论(0编辑  收藏  举报