C++: Accounts Merge

Thought Process

This problem requires merging accounts that share common emails. The key is to use Union-Find (Disjoint Set Union) to efficiently group connected emails. First, we initialize each email as its own parent. Then, we union emails from the same account. After grouping, we collect all emails under their root parent and format the results. The path compression in the 'find' operation ensures optimal performance.

class Solution {
public:
    // find method is using path compression
    // With path compression, instead of following the chain every time, 
    // we make each email directly point to the root when we find it. 
    // This way, next time it's an  instant lookup.
    string find(unordered_map<string, string> &parentMap, string email) {

        if (parentMap[email] != email) {
            parentMap[email] = find(parentMap, parentMap[email]);
        }
        return parentMap[email];

        // Traditional find method.
        // while(parentMap[email]!=email)
        //     email = parentMap[email];
        
        // return parentMap[email];
    }
    vector<vector<string>> accountsMerge(vector<vector<string>>& accounts) {

        unordered_map<string, string> mailToPerson; // Mail --> Person
        unordered_map<string, string> parentMap;    // Stores Parent of Each Email
        unordered_map<string, set<string>> parentToAllEmails; // ParentEmail --> All Emails
        vector<vector<string>> ans; // To store the answer in desired format.

        // Step-1: Initialization
        for (int i = 0; i < accounts.size(); i++) {

            for (int j = 1; j < accounts[i].size(); j++) {

                mailToPerson.insert({accounts[i][j], accounts[i][0]});
                parentMap[accounts[i][j]] = accounts[i][j]; // Initially, each email points to itself.
            }
        }

        // Step-2: Assign Parents (Union Operation)
        for (int i = 0; i < accounts.size(); i++) {

            string parentOfFirstEmail = find(parentMap, accounts[i][1]);  // Find parent of first email 
            for (int j = 2; j < accounts[i].size(); j++) {

                string parentOfOtherEmails = find(parentMap, accounts[i][j]);
                parentMap[parentOfOtherEmails] = parentOfFirstEmail;
            }
        }

        // Step-3: Group Emails under their Parent
        for (vector<string> entity : accounts) {

            string parentOfFirstEmail = find(parentMap, entity[1]);
            for (int i = 1; i < entity.size(); i++) {
                parentToAllEmails[parentOfFirstEmail].insert(entity[i]);
            }
        }

        // Step-4: Build the result in the required format.
        for (auto itr = parentToAllEmails.begin(); itr != parentToAllEmails.end(); itr++) {

            string personName = mailToPerson[itr->first];
            set<string> emails = itr->second;

            vector<string> mergedAccount(emails.begin(),emails.end());
            mergedAccount.insert(mergedAccount.begin(), personName);
            ans.push_back(mergedAccount);
        }
        return ans;
    }
};

Code Complexity

Time Complexity: O(n α(n))

Where n is the total number of emails and α is the inverse Ackermann function (nearly constant time due to path compression).

Space Complexity: O(n)

We use O(n) space to store parent relationships, email mappings, and the final result.

Code copied!