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) {gs
            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<>();
        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));
            }
            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];
            int max = 0;
            for (int i = 0; i < ids.length; i++) {
                count[find(i)]++;
                max = Math.max(max, count[find(i)]);
            }
            return max;
        }
    }
}

@@ -1181,11 +1181,9 @@
 s) %7B
-gs
 %0A
+
     
@@ -2081,24 +2081,49 @@
 HashMap%3C%3E();
+ // Get index with value.
 %0A        for
@@ -2374,24 +2374,47 @@
 ms%5Bi%5D + 1));
+ // Use index as cc id.
 %0A           
@@ -3556,24 +3556,24 @@
 axUnion() %7B%0A
-
             
@@ -3606,16 +3606,43 @@
 length%5D;
+ // # of nodes for each cc.
 %0A       

update longest consecutive sequence