最长连续序列

题目

给定一个未排序的整数数组,找出最长连续序列的长度。

要求算法的时间复杂度为 O(n)

示例:

输入: [100, 4, 200, 1, 3, 2]
输出: 4
解释: 最长连续序列是 [1, 2, 3, 4]。它的长度为 4。

来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/longest-consecutive-sequence
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。

思路

估计首先想到的都是排序吧,但是时间复杂度是O(nlogn)。那么想到的只有Hash了,这题HashMapHashSet都有解法。

HashSet解法

  1. 首先将所有元素放入HashSet,使查询时间变为O(1)
  2. 遍历数组,对每个元素,向左和向右扩展(查询是否在HashSet里面,若在则删除,避免相同连续序列的重复计算),计算扩展的长度,更新最长连续序列的值。
class Solution {
    
    // public static List asList(final int[] is)
    // {
    //     return new AbstractList() {
    //         public Integer get(int i) { return is[i]; }
    //         public int size() { return is.length; }
    //     };
    // }
    
    public int longestConsecutive(int[] nums) {
        
        int maxLen = 0;
        Set<Integer> set = new HashSet<>();
        for (int i = 0; i< nums.length; i++) set.add(nums[i]);
        // set.addAll(asList(nums));
        
        for (int i = 0; i < nums.length; i++) {
            if (set.contains(nums[i])) {
                int left = nums[i] - 1;
                int right = nums[i] + 1;
                
                // 在set里面,会删除返回true,避免重复计算
                while (set.remove(left)) left--;
                while (set.remove(right)) right++;
                
                int curMaxLen = right - left - 1;
                maxLen = Math.max(maxLen, curMaxLen);
            }
        }
        
        return maxLen;
    }
}

HashMap解法

HashMap里面,Key:元素的值,Value:该元素所在连续序列长度

遍历数组:
当元素e已经在HashMap时,不理会;
当元素e不在HashMap时:

  1. 检查其相邻的值是否在HashMap里,若不在则返回0;若存在则返回现在的序列长度
  2. 计算加上元素e时的序列长度,更新最大连续序列长度的值;
  3. 更新元素e及该连续序列的边界元素所对应的序列长度
class Solution {
    public int longestConsecutive(int[] nums) {
    
        int maxLen = 0;
        Map<Integer, Integer> map = new HashMap<>();
        
        for (int i = 0; i < nums.length; i++) {
            if (!map.containsKey(nums[i])) {
                int left = map.getOrDefault(nums[i] - 1, 0);
                int right = map.getOrDefault(nums[i] + 1, 0);
                
                int curMaxLen = 1 + left + right;
                maxLen = Math.max(maxLen, curMaxLen);
                
                // 更新连续序列的边界元素 对应的 连续序列长度
                map.put(nums[i] - left, curMaxLen);
                map.put(nums[i], curMaxLen);
                map.put(nums[i] + right, curMaxLen);
            }
        }
        
        return maxLen;
    }
}

并查集

还有一种并查集的解法,将连续序列合并成一个集合,返回最大的那个集合的长度即可。

Tips:当元素的值很大时不好建立数组,我们可以通过HashMapnums[i]映射成i

class Solution {
    
    int[] union;  // 并查集
    int[] size;  // 序列长度
    
    public void init(int n) {
        union = new int[n];
        size = new int[n];
        for (int i = 0; i < n; i++) {
            union[i] = i;
            size[i] = 1;
        }
    }
    
    // 查找根
    public int get(int x) {
        return union[x] = (union[x] == x ? x : get(union[x]));
    }
    
    // 将相邻元素合并到一个集合
    public void merge(int a, int b) {
        int root1 = get(a), root2 = get(b);
        if (root1 == root2) return ;
        union[root1] = root2;
        size[root2] += size[root1];
    }
    
    // 返回最大集合的长度
    public int getMaxSize() {
        int maxSize = 0;
        for (int i = 0; i < size.length; i++) {
            maxSize = Math.max(maxSize, size[i]);
        }
        return maxSize;
    }
    
    public int longestConsecutive(int[] nums) {
        
        // HashMap将 nums[i]映射为 i
        Map<Integer, Integer> map = new HashMap<>();
        int n = nums.length;
        this.init(n);
        
        for (int i = 0; i < n; i++) {
            if (map.containsKey(nums[i])) continue;
            map.put(nums[i], i);

            // 如果 map里有相邻元素,合并到一个集合
            if (map.containsKey(nums[i] - 1)) this.merge(i, map.get(nums[i] - 1));
            if (map.containsKey(nums[i] + 1)) this.merge(i, map.get(nums[i] + 1));
        }
        
        return this.getMaxSize();
    }
}

你可能感兴趣的:(算法训练)