public static int GetMaximumGroup(IList <int[]> pairs, int n)
        {
            if (n <= 0 || pairs == null || pairs.Count == 0)
            {
                return(-1);
            }

            var unionFind = new QuickUnion(n);

            foreach (var item in pairs)
            {
                unionFind.Union(item[0], item[1]);
            }

            var groupCount = new Dictionary <int, int>();

            // quick find and also path compression
            for (int i = 0; i < n; i++)
            {
                unionFind.QuickFindAndPathCompression(i);
            }

            // Find maximum group number
            for (int i = 0; i < n; i++)
            {
                var rootId = unionFind.QuickFind(i);
                if (!groupCount.ContainsKey(rootId))
                {
                    groupCount.Add(rootId, 1);
                }
                else
                {
                    groupCount[rootId]++;
                }
            }

            return(groupCount.Values.Max());
        }
        /// <summary>
        /// Leetcode 721
        /// https://leetcode.com/problems/accounts-merge/
        /// </summary>
        /// <param name="accounts"></param>
        /// <returns></returns>
        public static IList <IList <string> > AccountsMerge(IList <IList <string> > accounts)
        {
            var emailSet = getAllEmails(accounts);

            // https://leetcode.com/problems/accounts-merge/discuss/164699/C-Solution-(Union-Find)-beats-91.49
            var emailList = emailSet.ToList();

            var ordCmp = StringComparer.Ordinal;

            emailList.Sort(ordCmp);

            var emailNameMap = getEmailNameMap(accounts);
            var emailIdmap   = new Dictionary <string, int>();

            int index       = 0;
            var emailLookup = new List <string>();

            foreach (var item in emailList)
            {
                emailIdmap.Add(item, index);
                index++;
                emailLookup.Add(item);
            }

            var unionFind = new QuickUnion(emailSet.Count);

            unionFind.EmailIdMap = emailIdmap;

            foreach (var list in accounts)
            {
                for (int i = 1; i < list.Count - 1; i++)
                {
                    var email1 = list[i];
                    var email2 = list[i + 1];

                    var connected = unionFind.Connected(email1, email2);
                    if (!connected)
                    {
                        unionFind.Union(email1, email2);
                    }
                }
            }

            // flat the tree
            for (int i = 0; i < emailSet.Count; i++)
            {
                unionFind.QuickFindAndPathCompression(i);
            }

            var dict   = new Dictionary <int, List <string> >();
            var parent = unionFind.GetParent();

            for (int i = 0; i < parent.Length; i++)
            {
                var key = parent[i];
                if (!dict.ContainsKey(key))
                {
                    dict.Add(key, new List <string>());
                }

                dict[key].Add(emailLookup[i]);
            }

            // output a list
            var result = new List <IList <string> >();

            foreach (var item in dict.Keys)
            {
                var list  = new List <string>();
                var email = emailLookup[item];
                var name  = emailNameMap[email];

                list.Add(name);

                var values = dict[item];
                list.AddRange(values);

                result.Add(list);
            }

            return(result);
        }