Email Account Merging

Last Updated : 23 Jul, 2025

Given a 2D array arr[][], where each element arr[i] is an array of strings representing an account, where the first element arr[i][0] is a name, and the rest of the elements are emails associated with this name. Also, two accounts belong to the same person if there is a common email associated with both accounts. A person can have any number of accounts initially, but all have the same name.
The task is to merge these accounts and print them in the following format: the first element of each account is the name, and the rest of the elements are emails in lexicographically sorted order.

Note: Accounts themselves can be returned in any order. Even if two accounts have the same name, they may belong to different people as different people could have the same name.

Example:

Input: arr[][] =
[["John", "johnsmith@mail.com", "john_newyork@mail.com"],
["John", "johnsmith@mail.com", "john00@mail.com"],
["Mary", "mary@mail.com"],
["John", "johnnybravo@mail.com"]]
Output:
[["John", "john00@mail.com", "john_newyork@mail.com", "johnsmith@mail.com"],["Mary","mary@mail.com"],
["John","johnnybravo@mail.com"]]
Explanation: There are three accounts with the same name "John", and two of them share a common email which is "johnsmith@mail.com", thus these two accounts are merged, and remaining two will remain unchanged.

Input: arr[][] =
[["Gabe", "Gabe00@m.co", "Gabe3@m.co", "Gabe1@m.co"],["Kevin","Kevin3@m.co","Kevin5@m.co", "Kevin0@m.co"], ["Ethan","Ethan5@m.co","Ethan4@m.co","Ethan0@m.co"],["Hanzo","Hanzo3@m.co","Hanzo1@m.co","Hanzo0@m.co"],["Fern","Fern5@m.co","Fern1@m.co","Fern0@m.co"]]
Output:
[["Ethan","Ethan0@m.co","Ethan4@m.co","Ethan5@m.co"],["Gabe","Gabe0@m.co","Gabe1@m.co","Gabe3@m.co"],["Hanzo","Hanzo0@m.co","Hanzo1@m.co","Hanzo3@m.co"],["Kevin","Kevin0@m.co","Kevin3@m.co","Kevin5@m.co"],["Fern","Fern0@m.co","Fern1@m.co","Fern5@m.co"]]
Explanation: There is no common email in any of the users.

Try It Yourself
redirect icon

The idea is to represent the list of accounts arr[][] in the form of Graph, where each node represents an email, and an edge signifies that two emails are connected i.e. they belong to same person. To do so, firstly connect all the emails initially associated to the same name in Star Graph format.
Now explore each connected component to find all the emails that belong to one person using DFS. To do so, iterate over all of the nodes and if the node has already been visited, skip it. Otherwise, perform a DFS traversal over the connected component and store all the visited emails together, as they all belong to one person. Each time we visit an email during a DFS, we will mark it as visited to ensure that we do not search the same connected component more than once.

C++
// C++ program to merge the accounts
#include <bits/stdc++.h>
using namespace std;

// Function to perform DFS
void DFS(vector<string> &account, string &email, 
        unordered_set<string> &visited, 
        unordered_map<string, vector<string>> &adjacent) {

    // Mark the email as visited
    visited.insert(email);

    // Add the email vector that contains 
    // the current component's emails
    account.push_back(email);

    // perform dfs for connected emails
    for (string &x : adjacent[email]) {
        if (visited.find(x) == visited.end()) {
            DFS(account, x, visited, adjacent);
        }
    }
}

// Function to merge the email accounts
vector<vector<string>> accountsMerge(vector<vector<string>> &arr) {

    unordered_map<string, vector<string>> adjacent;
    unordered_set<string> visited;

    // Building adjacency list
    for (vector<string> &i : arr) {

        // Adding edge between first email to 
        // all other emails in the account
        string firstMail = i[1];
        for (int j = 2; j < i.size(); j++) {
            string email = i[j];
            adjacent[firstMail].push_back(email);
            adjacent[email].push_back(firstMail);
        }
    }

    // Traverse over all the accounts to store components
    vector<vector<string>> accounts;
    for (vector<string> &i : arr) {
        string accountName = i[0];
        string firstMail = i[1];

        // If email is visited, then it's a part of different component
        // Hence perform DFS only if email is not visited yet
        if (visited.find(firstMail) == visited.end()) {
            vector<string> account;

            // Adding account name at the 0th index
            account.push_back(accountName);

            DFS(account, firstMail, visited, adjacent);
            accounts.push_back(account);
        }
    }

    return accounts;
}

int main() {
    vector<vector<string>> arr = {
    {"John", "johnsmith@mail.com", "john_newyork@mail.com"}, 
    {"John", "johnsmith@mail.com", "john00@mail.com"}, 
    {"Mary", "mary@mail.com"}, 
    {"John", "johnnybravo@mail.com"}};
    arr = accountsMerge(arr);
    for (vector<string> &i : arr) {
        for (string &j : i) {
            cout << j << " ";
        }
        cout << endl;
    }
    return 0;
}
Java
// Java program to merge the accounts
import java.util.*;

class GfG {

    // Function to perform DFS
    private static void DFS(ArrayList<String> account, String email, 
                            Set<String> visited, 
                            Map<String, ArrayList<String>> adjacent) {

        // Mark the email as visited
        visited.add(email);

        // Add the email vector that contains 
        // the current component's emails
        account.add(email);

        // Perform DFS for connected emails
        for (String x : adjacent.getOrDefault(email, new ArrayList<>())) {
            if (!visited.contains(x)) {
                DFS(account, x, visited, adjacent);
            }
        }
    }

    // Function to merge the email accounts
    public static ArrayList<ArrayList<String>> accountsMerge(ArrayList<ArrayList<String>> arr) {

        Map<String, ArrayList<String>> adjacent = new HashMap<>();
        Set<String> visited = new HashSet<>();

        // Building adjacency list
        for (ArrayList<String> i : arr) {

            // Adding edge between first email to 
            // all other emails in the account
            String firstMail = i.get(1);
            for (int j = 2; j < i.size(); j++) {
                String email = i.get(j);
                adjacent.computeIfAbsent(firstMail, k -> new ArrayList<>()).add(email);
                adjacent.computeIfAbsent(email, k -> new ArrayList<>()).add(firstMail);
            }
        }

        // Traverse over all the accounts to store components
        ArrayList<ArrayList<String>> accounts = new ArrayList<>();
        for (ArrayList<String> i : arr) {
            String accountName = i.get(0);
            String firstMail = i.get(1);

            // If email is visited, then it's a part of different component
            // Hence perform DFS only if email is not visited yet
            if (!visited.contains(firstMail)) {
                ArrayList<String> account = new ArrayList<>();

                // Adding account name at the 0th index
                account.add(accountName);

                DFS(account, firstMail, visited, adjacent);
                
                accounts.add(account);
            }
        }

        return accounts;
    }

    public static void main(String[] args) {
        ArrayList<ArrayList<String>> arr = new ArrayList<>();
        arr.add(new ArrayList<>(Arrays.asList("John", "johnsmith@mail.com", "john_newyork@mail.com")));
        arr.add(new ArrayList<>(Arrays.asList("John", "johnsmith@mail.com", "john00@mail.com")));
        arr.add(new ArrayList<>(Arrays.asList("Mary", "mary@mail.com")));
        arr.add(new ArrayList<>(Arrays.asList("John", "johnnybravo@mail.com")));

        arr = accountsMerge(arr);
        for (ArrayList<String> i : arr) {
            for (String j : i) {
                System.out.print(j + " ");
            }
            System.out.println();
        }
    }
}
Python
# Python program to merge the accounts
from collections import defaultdict

# Function to perform DFS
def DFS(account, email, visited, adjacent):

    # Mark the email as visited
    visited.add(email)

    # Add the email vector that contains 
    # the current component's emails
    account.append(email)

    # perform dfs for connected emails
    for x in adjacent[email]:
        if x not in visited:
            DFS(account, x, visited, adjacent)

# Function to merge the email accounts
def accountsMerge(arr):
    adjacent = defaultdict(list)
    visited = set()

    # Building adjacency list
    for i in arr:

        # Adding edge between first email to 
        # all other emails in the account
        firstMail = i[1]
        for j in range(2, len(i)):
            email = i[j]
            adjacent[firstMail].append(email)
            adjacent[email].append(firstMail)

    # Traverse over all the accounts to store components
    accounts = []
    for i in arr:
        accountName = i[0]
        firstMail = i[1]

        # If email is visited, then it's a part of different component
        # Hence perform DFS only if email is not visited yet
        if firstMail not in visited:
            account = []

            # Adding account name at the 0th index
            account.append(accountName)

            DFS(account, firstMail, visited, adjacent)

            accounts.append(account)

    return accounts

if __name__ == "__main__":
    arr = [
        ["John", "johnsmith@mail.com", "john_newyork@mail.com"], 
        ["John", "johnsmith@mail.com", "john00@mail.com"], 
        ["Mary", "mary@mail.com"], 
        ["John", "johnnybravo@mail.com"]
    ]
    arr = accountsMerge(arr)
    for i in arr:
        print(" ".join(i))
C#
// C# program to merge the accounts
using System;
using System.Collections.Generic;

class GfG {

    // Function to perform DFS
    static void DFS(List<string> account, string email, 
                    HashSet<string> visited, 
                    Dictionary<string, List<string>> adjacent) {

        // Mark the email as visited
        visited.Add(email);

        // Add the email vector that contains 
        // the current component's emails
        account.Add(email);

        // Perform DFS for connected emails
        if (adjacent.ContainsKey(email)) {  // Check if email has neighbors
            foreach (string x in adjacent[email]) {
                if (!visited.Contains(x)) {
                    DFS(account, x, visited, adjacent);
                }
            }
        }
    }

    // Function to merge the email accounts
    public static List<List<string>> AccountsMerge(List<List<string>> arr) {

        var adjacent = new Dictionary<string, List<string>>();
        var visited = new HashSet<string>();

        // Building adjacency list
        foreach (var i in arr) {

            // Adding edge between first email to 
            // all other emails in the account
            string firstMail = i[1];
            if (!adjacent.ContainsKey(firstMail)) {
                adjacent[firstMail] = new List<string>();
            }
            for (int j = 2; j < i.Count; j++) {
                string email = i[j];
                if (!adjacent.ContainsKey(email)) {
                    adjacent[email] = new List<string>();
                }
                adjacent[firstMail].Add(email);
                adjacent[email].Add(firstMail);
            }
        }

        // Traverse over all the accounts to store components
        var accounts = new List<List<string>>();
        foreach (var i in arr) {
            string accountName = i[0];
            string firstMail = i[1];

            // If email is visited, then it's a part of different component
            // Hence perform DFS only if email is not visited yet
            if (!visited.Contains(firstMail)) {
                var account = new List<string>();

                // Adding account name at the 0th index
                account.Add(accountName);

                DFS(account, firstMail, visited, adjacent);

                accounts.Add(account);
            }
        }

        return accounts;
    }

    static void Main() {
        var arr = new List<List<string>> {
            new List<string> { "John", "johnsmith@mail.com", "john_newyork@mail.com" },
            new List<string> { "John", "johnsmith@mail.com", "john00@mail.com" },
            new List<string> { "Mary", "mary@mail.com" },
            new List<string> { "John", "johnnybravo@mail.com" }
        };

        var result = AccountsMerge(arr);
        foreach (var account in result) {
            Console.WriteLine(string.Join(" ", account));
        }
    }
}
JavaScript
// JavaScript program to merge the accounts

// Function to perform DFS
function DFS(account, email, visited, adjacent) {

    // Mark the email as visited
    visited.add(email);

    // Add the email vector that contains 
    // the current component's emails
    account.push(email);

    // Perform DFS for connected emails
    if (adjacent[email]) {  // Check if email has neighbors
        for (let x of adjacent[email]) {
            if (!visited.has(x)) {
                DFS(account, x, visited, adjacent);
            }
        }
    }
}

// Function to merge the email accounts
function accountsMerge(arr) {
    const adjacent = {};
    const visited = new Set();

    // Building adjacency list
    for (const i of arr) {

        // Adding edge between first email to 
        // all other emails in the account
        const firstMail = i[1];
        if (!adjacent[firstMail]) adjacent[firstMail] = [];
        for (let j = 2; j < i.length; j++) {
            const email = i[j];
            if (!adjacent[email]) adjacent[email] = [];
            adjacent[firstMail].push(email);
            adjacent[email].push(firstMail);
        }
    }

    // Traverse over all the accounts to store components
    const accounts = [];
    for (const i of arr) {
        const accountName = i[0];
        const firstMail = i[1];

        // If email is visited, then it's a part of different component
        // Hence perform DFS only if email is not visited yet
        if (!visited.has(firstMail)) {
            const account = [];

            // Adding account name at the 0th index
            account.push(accountName);

            DFS(account, firstMail, visited, adjacent);

            accounts.push(account);
        }
    }

    return accounts;
}

// Driver Code
const arr = [
    ["John", "johnsmith@mail.com", "john_newyork@mail.com"],
    ["John", "johnsmith@mail.com", "john00@mail.com"],
    ["Mary", "mary@mail.com"],
    ["John", "johnnybravo@mail.com"]
];

const result = accountsMerge(arr);
result.forEach(account => console.log(account.join(" ")));

Output
John john00@mail.com john_newyork@mail.com johnsmith@mail.com 
Mary mary@mail.com 
John johnnybravo@mail.com 

Time Complexity: O(n * k * log(n * k)), where n * k is total number of emails.
Space Complexity: O(n * k), to build an adjacency list.

[Approach - 2] Using Disjoint Set Union

The idea is to iterate through the given accounts, and for each email encountered, either add it to an existing set (if the email is already encountered) or creates a new set. We will use Disjoint Set Union (DSU) to group emails that belong to the same account. After grouping emails, construct the final result by combining the emails within each set with the account name.

C++
// C++ program to merge the arr
#include <bits/stdc++.h>
using namespace std;

// Class to implement Disjoint Set
class DisjointSet {
vector<int> parent, size;
public:
DisjointSet(int n) {
    parent.resize(n);
    size.resize(n, 1);

    for (int i = 0; i < n; i++) {
        parent[i] = i;
    }
}

int findParent(int node) {

    // Find the root of the set to which 'node' belongs
    if (node == parent[node]) {
        return node;
    }

    // Make the parent of 'node' the root
    return parent[node] = findParent(parent[node]);
}

void unionBySize(int u, int v) {
    
    // Union by size to merge two sets
    int par_u = findParent(u);
    int par_v = findParent(v);

    if (par_u == par_v) {
        return;
    }
    if (size[par_u] < size[par_v]) {
        parent[par_u] = par_v;
        size[par_v] += size[par_u];
    }
    else {
        parent[par_v] = par_u;
        size[par_u] += size[par_v];
    }
}
};

vector<vector<string>> accountsMerge(vector<vector<string>>& arr) {
    unordered_map<string, int> mails;
    int n = arr.size();
    DisjointSet ds(n);

    // Iterate through arr and build disjoint sets
    for (int i = 0; i < n; i++) {
        for (int j = 1; j < arr[i].size(); j++) {
            string mail = arr[i][j];
            if (mails.find(mail) == mails.end()) {
                mails[mail] = i;
            }
            else {
                ds.unionBySize(i, mails[mail]);
            }
        }
    }

    // Group merged emails based on disjoint sets
    vector<string> mergedMails[n];
    for (auto it : mails) {
        int node = ds.findParent(it.second);
        string mail = it.first;
        mergedMails[node].push_back(mail);
    }

    // Construct the final result
    vector<vector<string>> accounts;
    for (int i = 0; i < n; i++) {
        if (mergedMails[i].size() == 0) {
            continue;
        }
        vector<string> temp;
        temp.push_back(arr[i][0]);
        for (auto it : mergedMails[i]) {
            temp.push_back(it);
        }
        accounts.push_back(temp);
    }
    return accounts;
}

int main() {
    vector<vector<string>> arr = {
    {"John", "johnsmith@mail.com", "john_newyork@mail.com"}, 
    {"John", "johnsmith@mail.com", "john00@mail.com"}, 
    {"Mary", "mary@mail.com"}, 
    {"John", "johnnybravo@mail.com"}};
    arr = accountsMerge(arr);
    for (vector<string> &i : arr) {
        for (string &j : i) {
            cout << j << " ";
        }
        cout << endl;
    }
    return 0;
}
Java
// Java program to merge the arr
import java.util.*;

class GfG {

    // Class to implement Disjoint Set
    static class DisjointSet {
        ArrayList<Integer> parent, size;

        DisjointSet(int n) {
            parent = new ArrayList<>(Collections.nCopies(n, 0));
            size = new ArrayList<>(Collections.nCopies(n, 1));

            for (int i = 0; i < n; i++) {
                parent.set(i, i);
            }
        }

        int findParent(int node) {
            // Find the root of the set to which 'node' belongs
            if (node == parent.get(node)) {
                return node;
            }

            // Make the parent of 'node' the root
            parent.set(node, findParent(parent.get(node)));
            return parent.get(node);
        }

        void unionBySize(int u, int v) {
            // Union by size to merge two sets
            int par_u = findParent(u);
            int par_v = findParent(v);

            if (par_u == par_v) {
                return;
            }
            if (size.get(par_u) < size.get(par_v)) {
                parent.set(par_u, par_v);
                size.set(par_v, size.get(par_v) + size.get(par_u));
            } else {
                parent.set(par_v, par_u);
                size.set(par_u, size.get(par_u) + size.get(par_v));
            }
        }
    }

    static ArrayList<ArrayList<String>> accountsMerge(ArrayList<ArrayList<String>> arr) {
        HashMap<String, Integer> mails = new HashMap<>();
        int n = arr.size();
        DisjointSet ds = new DisjointSet(n);

        // Iterate through arr and build disjoint sets
        for (int i = 0; i < n; i++) {
            for (int j = 1; j < arr.get(i).size(); j++) {
                String mail = arr.get(i).get(j);
                if (!mails.containsKey(mail)) {
                    mails.put(mail, i);
                } else {
                    ds.unionBySize(i, mails.get(mail));
                }
            }
        }

        // Group merged emails based on disjoint sets
        ArrayList<ArrayList<String>> mergedMails = new ArrayList<>(Collections.nCopies(n, null));
        for (int i = 0; i < n; i++) {
            mergedMails.set(i, new ArrayList<>());
        }
        for (Map.Entry<String, Integer> it : mails.entrySet()) {
            int node = ds.findParent(it.getValue());
            String mail = it.getKey();
            mergedMails.get(node).add(mail);
        }

        // Construct the final result
        ArrayList<ArrayList<String>> accounts = new ArrayList<>();
        for (int i = 0; i < n; i++) {
            if (mergedMails.get(i).isEmpty()) {
                continue;
            }
            ArrayList<String> temp = new ArrayList<>();
            temp.add(arr.get(i).get(0));
            temp.addAll(mergedMails.get(i));
            accounts.add(temp);
        }
        return accounts;
    }

    public static void main(String[] args) {
        ArrayList<ArrayList<String>> arr = new ArrayList<>(Arrays.asList(
            new ArrayList<>(Arrays.asList("John", "johnsmith@mail.com", "john_newyork@mail.com")),
            new ArrayList<>(Arrays.asList("John", "johnsmith@mail.com", "john00@mail.com")),
            new ArrayList<>(Arrays.asList("Mary", "mary@mail.com")),
            new ArrayList<>(Arrays.asList("John", "johnnybravo@mail.com"))
        ));

        ArrayList<ArrayList<String>> result = accountsMerge(arr);
        for (ArrayList<String> i : result) {
            System.out.println(String.join(" ", i));
        }
    }
}
Python
# Python program to merge the arr

class DisjointSet:
    def __init__(self, n):
        self.parent = [i for i in range(n)]
        self.size = [1] * n

    def findParent(self, node):
        # Find the root of the set to which 'node' belongs
        if node == self.parent[node]:
            return node

        # Make the parent of 'node' the root
        self.parent[node] = self.findParent(self.parent[node])
        return self.parent[node]

    def unionBySize(self, u, v):
        # Union by size to merge two sets
        par_u = self.findParent(u)
        par_v = self.findParent(v)

        if par_u == par_v:
            return
        if self.size[par_u] < self.size[par_v]:
            self.parent[par_u] = par_v
            self.size[par_v] += self.size[par_u]
        else:
            self.parent[par_v] = par_u
            self.size[par_u] += self.size[par_v]


def accountsMerge(arr):
    mails = {}
    n = len(arr)
    ds = DisjointSet(n)

    # Iterate through arr and build disjoint sets
    for i in range(n):
        for j in range(1, len(arr[i])):
            mail = arr[i][j]
            if mail not in mails:
                mails[mail] = i
            else:
                ds.unionBySize(i, mails[mail])

    # Group merged emails based on disjoint sets
    mergedMails = [[] for _ in range(n)]
    for mail, index in mails.items():
        node = ds.findParent(index)
        mergedMails[node].append(mail)

    # Construct the final result
    accounts = []
    for i in range(n):
        if not mergedMails[i]:
            continue
        temp = [arr[i][0]]
        temp.extend(mergedMails[i])
        accounts.append(temp)

    return accounts


if __name__ == "__main__":
    arr = [
        ["John", "johnsmith@mail.com", "john_newyork@mail.com"],
        ["John", "johnsmith@mail.com", "john00@mail.com"],
        ["Mary", "mary@mail.com"],
        ["John", "johnnybravo@mail.com"]
    ]
    result = accountsMerge(arr)
    for i in result:
        print(" ".join(i))
C#
// C# program to merge the arr
using System;
using System.Collections.Generic;

class GfG {
    
    // Class to implement Disjoint Set
    class DisjointSet {
        private int[] parent, size;

        public DisjointSet(int n) {
            parent = new int[n];
            size = new int[n];

            for (int i = 0; i < n; i++) {
                parent[i] = i;
                size[i] = 1;
            }
        }

        public int findParent(int node) {
            if (node == parent[node]) {
                return node;
            }
            // Path compression
            return parent[node] = findParent(parent[node]);
        }

        public void unionBySize(int u, int v) {
            int par_u = findParent(u);
            int par_v = findParent(v);

            if (par_u == par_v) {
                return;
            }
            if (size[par_u] < size[par_v]) {
                parent[par_u] = par_v;
                size[par_v] += size[par_u];
            } else {
                parent[par_v] = par_u;
                size[par_u] += size[par_v];
            }
        }
    }

    public static List<List<string>> AccountsMerge(List<List<string>> arr) {
        var mails = new Dictionary<string, int>();
        int n = arr.Count;
        var ds = new DisjointSet(n);

        // Iterate through arr and build disjoint sets
        for (int i = 0; i < n; i++) {
            for (int j = 1; j < arr[i].Count; j++) {
                string mail = arr[i][j];
                if (!mails.ContainsKey(mail)) {
                    mails[mail] = i;
                } else {
                    ds.unionBySize(i, mails[mail]);
                }
            }
        }

        // Group merged emails based on disjoint sets
        var mergedMails = new List<string>[n];  // Array of lists
        for (int i = 0; i < n; i++) {
            mergedMails[i] = new List<string>();
        }
        
        foreach (var it in mails) {
            int node = ds.findParent(it.Value);
            string mail = it.Key;
            mergedMails[node].Add(mail);
        }

        // Construct the final result
        var accounts = new List<List<string>>();
        for (int i = 0; i < n; i++) {
            if (mergedMails[i].Count == 0) {
                continue;
            }
            var temp = new List<string>();
            temp.Add(arr[i][0]);  // Add account name
            temp.AddRange(mergedMails[i]);
            accounts.Add(temp);
        }

        return accounts;
    }

    public static void Main(string[] args) {
        var arr = new List<List<string>> {
            new List<string> { "John", "johnsmith@mail.com", "john_newyork@mail.com" },
            new List<string> { "John", "johnsmith@mail.com", "john00@mail.com" },
            new List<string> { "Mary", "mary@mail.com" },
            new List<string> { "John", "johnnybravo@mail.com" }
        };
        var result = AccountsMerge(arr);
        foreach (var i in result) {
            Console.WriteLine(string.Join(" ", i));
        }
    }
}
JavaScript
// JavaScript program to merge the arr

class DisjointSet {
    constructor(n) {
        this.parent = Array.from({ length: n }, (_, i) => i);
        this.size = Array(n).fill(1);
    }

    findParent(node) {
        // Find the root of the set to which 'node' belongs
        if (node === this.parent[node]) {
            return node;
        }

        // Make the parent of 'node' the root
        return (this.parent[node] = this.findParent(this.parent[node]));
    }

    unionBySize(u, v) {
        // Union by size to merge two sets
        const par_u = this.findParent(u);
        const par_v = this.findParent(v);

        if (par_u === par_v) {
            return;
        }
        if (this.size[par_u] < this.size[par_v]) {
            this.parent[par_u] = par_v;
            this.size[par_v] += this.size[par_u];
        } else {
            this.parent[par_v] = par_u;
            this.size[par_u] += this.size[par_v];
        }
    }
}

function accountsMerge(arr) {
    const mails = new Map();
    const n = arr.length;
    const ds = new DisjointSet(n);

    // Iterate through arr and build disjoint sets
    for (let i = 0; i < n; i++) {
        for (let j = 1; j < arr[i].length; j++) {
            const mail = arr[i][j];
            if (!mails.has(mail)) {
                mails.set(mail, i);
            } else {
                ds.unionBySize(i, mails.get(mail));
            }
        }
    }

    // Group merged emails based on disjoint sets
    const mergedMails = Array.from({ length: n }, () => []);
    for (const [mail, index] of mails.entries()) {
        const node = ds.findParent(index);
        mergedMails[node].push(mail);
    }

    // Construct the final result
    const accounts = [];
    for (let i = 0; i < n; i++) {
        if (mergedMails[i].length === 0) {
            continue;
        }
        const temp = [arr[i][0]];
        accounts.push(temp.concat(mergedMails[i]));
    }
    return accounts;
}

// Main function
const arr = [
    ["John", "johnsmith@mail.com", "john_newyork@mail.com"],
    ["John", "johnsmith@mail.com", "john00@mail.com"],
    ["Mary", "mary@mail.com"],
    ["John", "johnnybravo@mail.com"]
];
const result = accountsMerge(arr);
result.forEach(i => console.log(i.join(" ")));

Output
John john00@mail.com john_newyork@mail.com johnsmith@mail.com 
Mary mary@mail.com 
John johnnybravo@mail.com 

Time Complexity: O(n * k * log(n * k)), where n * k is total number of emails.
Space Complexity: O(n * k), to build an adjacency list.

Comment