721. Accounts Merge

Union Find

class UnionFind {
private:
    vector<int> parent; // Stores the parent of each node
    vector<int> rank;   // Stores the rank (or depth) of each node

public:
    // Constructor: Initialize parent and rank vectors
    UnionFind(int n)
    {
        parent.resize(n); // Resize parent vector to hold n elements
        rank.resize(n, 1); // Initialize rank vector with 1 for all elements
        for (int i = 0; i < n; i++) parent[i] = i; // Set each node to be its own parent
    }

    // Find the root of the set containing x, with path compression
    int find(int x)
    {
        if (x != parent[x])
        {
            parent[x] = find(parent[x]); // Path compression
        }
        return parent[x];
    }

    // Union the sets containing n1 and n2
    void unionNodes(int n1, int n2)
    {
        int p1 = find(n1); // Find the root of the set containing n1
        int p2 = find(n2); // Find the root of the set containing n2
        if (p1 == p2) return; // If they are already in the same set, do nothing

        // Union by rank
        if (rank[p1] > rank[p2])
        {
            parent[p2] = p1; // Attach smaller tree to larger tree
        }
        else if (rank[p1] < rank[p2])
        {
            parent[p1] = p2; // Attach smaller tree to larger tree
        }
        else
        {
            parent[p2] = p1; // Attach second tree to first and increase rank
            rank[p1]++;
        }
    }
};

class Solution {
public:
    // Merge accounts with the same email
    vector<vector<string>> accountsMerge(vector<vector<string>>& accounts)
    {
        int n = accounts.size(); // Number of accounts
        UnionFind uf(n); // Create UnionFind instance with n nodes
        unordered_map<string, int> emailMap; // Maps email to account index

        // Iterate through each account
        for (int i = 0; i < n; i++)
        {
            // Iterate through each email in the account
            for (int j = 1; j < accounts[i].size(); j++)
            {
                string email = accounts[i][j];
                if (emailMap.count(email))
                {
                    // Union the current account with the account that already has this email
                    uf.unionNodes(emailMap[email], i);
                }
                else
                {
                    // Map the email to the current account index
                    emailMap[email] = i;
                }
            }
        }

        unordered_map<int, vector<string>> mergeMap; // Maps root account index to list of emails
        // Collect emails for each connected component
        for (auto& [email, accountIdx] : emailMap)
        {
            mergeMap[uf.find(accountIdx)].push_back(email);
        }

        vector<vector<string>> res; // Result vector to store merged accounts
        // Prepare the final result
        for (auto& [accountIndex, emails] : mergeMap)
        {
            string user = accounts[accountIndex][0]; // Get the username
            vector<string> group = {user}; // Initialize group with the username
            sort(emails.begin(), emails.end()); // Sort emails in lexicographical order
            group.insert(group.end(), emails.begin(), emails.end()); // Add emails to group
            res.push_back(group); // Add group to result
        }

        return res; // Return the merged accounts
    }
};
  • T: O(nklog(nk))O(n \cdot k \cdot \log (n \cdot k))
  • S: O(NK)O(N \cdot K)