package com.freetymekiyan.algorithms.level.hard;
import org.junit.Assert;
import org.junit.Test;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
/**
* Given an unsorted array of integers, find the length of the longest consecutive elements sequence.
* <p>
* For example,
* Given [100, 4, 200, 1, 3, 2],
* The longest consecutive elements sequence is [1, 2, 3, 4]. Return its length: 4.
* <p>
* Your algorithm should run in O(n) complexity.
* <p>
* Company Tags: Google, Facebook
* Tags: Array, Union Find
* Similar Problems: (M) Binary Tree Longest Consecutive Sequence
*/
public class LongestConsecutiveSeq {
/**
* Set. O(n^2) Time worst case.
* Add numbers into a set first.
* Then for each number i:
* 1) Make sure it's the start of a streak by check i - 1.
* 2) If it's the start, check i + 1, i + 2 ... till reach j where it's not in the set.
* Update max length by j - i.
*/
public int longestConsecutive(int[] nums) {
if (nums == null || nums.length == 0) {
return 0;
}
Set<Integer> set = new HashSet<>();
for (int i : nums) {
set.add(i);
}
int max = 1;
for (int i : set) {
if (set.contains(i - 1)) {
continue;
}
int j = i + 1;
while (set.contains(j)) {
j++;
}
max = Math.max(j - i, max);
}
return max;
}
/**
* Union Find. O(n) Time.
* Take consecutive sequence as connected component.
* Use a map to store value to index mapping.
* For each number in nums:
* | If already in map, skip.
* | Put number and index in map.
* | If nums[i]+1 is also in map, union it with i.
* | If nums[i]-1 is also in map, union it with i.
* Go through the ids in union find to get the max count.
*/
public int longestConsecutiveB(int[] nums) {
UF uf = new UF(nums.length);
Map<Integer, Integer> locs = new HashMap<>(); // Get index with value.
for (int i = 0; i < nums.length; i++) {
if (locs.containsKey(nums[i])) {
continue;
}
locs.put(nums[i], i);
if (locs.containsKey(nums[i] + 1)) {
uf.union(i, locs.get(nums[i] + 1)); // Use index as cc id.
}
if (locs.containsKey(nums[i] - 1)) {
uf.union(i, locs.get(nums[i] - 1));
}
}
return uf.maxUnion();
}
@Test
public void testExamples() {
int[] a = {100, 4, 200, 1, 3, 2};
Assert.assertEquals(4, longestConsecutive(a));
}
private class UF {
private int[] ids;
public UF(int n) {
ids = new int[n];
for (int i = 0; i < n; i++) {
ids[i] = i;
}
}
private int find(int i) {
while (i != ids[i]) {
ids[i] = ids[ids[i]];
i = ids[i];
}
return i;
}
public boolean connected(int i, int j) {
return find(i) == find(j);
}
public void union(int p, int q) {
int i = find(p);
int j = find(q);
if (i == j) {
return;
}
ids[i] = j;
}
/**
* Returns the maximum size of a connected component.
* O(n) Time. O(n) Space.
*/
public int maxUnion() {
int[] count = new int[ids.length]; // # of nodes for each cc.
int max = 0;
for (int i = 0; i < ids.length; i++) {
count[find(i)]++;
max = Math.max(max, count[find(i)]);
}
return max;
}
}
}