leetcode721.账户合并(C/C++/Java/python)

PS:算法并非原创,仅作个人学习记录使用,侵删

题目描述
leetcode721.账户合并(C/C++/Java/python)_第1张图片
算法分析
个人的算法思路是:先合并相同账号,然后对每个账号的邮箱数组可以逐个进行字符串排序。
但是问题就在于:如何合并相同的账号?根据题目的意思,两个账号是否相同似乎只有一个评判标准:两个帐号是不是使用相同的邮箱地址。如果逐个比较的话,账号之间的配对相当多,似乎不是一个很好的方法。
但我也没办法想出更棒的想法了。
后来看了一些大佬的题解,没想到啊,竟然还能用并查集来解决。
并查集,yyds!
以下是官方题解:
leetcode721.账户合并(C/C++/Java/python)_第2张图片

代码实现
【C】

/*
C语言解法:哈希表+并查集
*/
#include 
#include 
#include 
#include 
#include 
#include 

#define MMAX(a, b)        ((a) > (b)? (a) : (b))
#define MMIN(a, b)        ((a) < (b)? (a) : (b))

#define RET_LEN             1000
#define RET_COL_LEN         1000

char *ret_[RET_LEN][RET_COL_LEN];
char **ret[RET_LEN];
int ret_col[RET_LEN];

int find(int *fa, int x) {
    if(x == fa[x]) {
        return fa[x];
    }

    fa[x] = find(fa, fa[x]);
    return fa[x];
}

void join(int *fa, int x, int y) {
    int xx = find(fa, x);
    int yy = find(fa, y);

    if(xx == yy) {
        return;
    }

    if(xx < yy) {
        fa[yy] = xx;
    } else {
        fa[xx] = yy;
    }
}

typedef struct _hash_st {
    char *key;
    int val;
    UT_hash_handle hh;
}hash_st;

int compare(hash_st *a, hash_st *b) {
    return strcmp(a->key, b->key);
}

//【算法思路】HASH + 字符串 + 并查集 + 排序。
// 遍历一个账户中的邮箱,如果已经出现过,则进行合并;如果未出现过,则新建hash表项
// 遍历并查集,将结果整合
char *** accountsMerge(char *** accounts, int accountsSize, int* accountsColSize, int* returnSize, int** returnColumnSizes){
    if(accountsSize <= 1) {
        *returnSize = accountsSize;
        *returnColumnSizes = accountsColSize;
        return accounts;
    }

    int *fa = (int *)calloc(accountsSize, sizeof(int));

    for(int i = 0; i < accountsSize; i++) {
        fa[i] = i;
    }

    //遍历accounts,按照邮箱建立hash表
    hash_st *head = NULL;

    for(int i = 0; i < accountsSize; i++) {
        for(int j = 1; j < accountsColSize[i]; j++) {
            char *key = accounts[i][j];

            hash_st *tmph;
            HASH_FIND(hh, head, key, strlen(key), tmph);
            
            if(tmph == NULL) {
                tmph = (hash_st *)calloc(1, sizeof(hash_st));
                tmph->key = key;
                tmph->val = i;

                HASH_ADD_KEYPTR(hh, head, tmph->key, strlen(tmph->key), tmph);
            } else {
                join(fa, i, tmph->val);
            }
        }
    }

    HASH_SORT(head, compare);

    //建立账户下标和结果下标的映射关系,结果下标 = map[账户下标] - 1
    int *map = (int *)calloc(accountsSize, sizeof(int));
    int rsize = 0;

    //遍历hash表,将所有结果分别放入不同的根结果中
    hash_st *hp0, *hp1;
    HASH_ITER(hh, head, hp0, hp1) {
        int fid = find(fa, hp0->val);

        //printf("(%d -> %d) %s\n", hp0->val, fid, hp0->key);

        if(map[fid] == 0) {
            map[fid] = rsize + 1;

            //首次有加入结果中
            ret[rsize] = ret_[rsize];
            ret_col[rsize] = 0;

            ret[rsize][ret_col[rsize]++] = accounts[fid][0];
            ret[rsize][ret_col[rsize]++] = hp0->key;
            rsize++;
        } else {
            int rid = map[fid] - 1;
            ret[rid][ret_col[rid]++] = hp0->key;
        }
    }

    *returnSize = rsize;
    *returnColumnSizes = ret_col;
    return ret;
}

C语言参考网址

【C++】

/*
C++解法:哈希表+并查集
*/
class UnionFind {
public:
    vector<int> parent;

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

    void unionSet(int index1, int index2) {
        parent[find(index2)] = find(index1);
    }

    int find(int index) {
        if (parent[index] != index) {
            parent[index] = find(parent[index]);
        }
        return parent[index];
    }
};

class Solution {
public:
    vector<vector<string>> accountsMerge(vector<vector<string>>& accounts) {
        map<string, int> emailToIndex;
        map<string, string> emailToName;
        int emailsCount = 0;
        for (auto& account : accounts) {
            string& name = account[0];
            int size = account.size();
            for (int i = 1; i < size; i++) {
                string& email = account[i];
                if (!emailToIndex.count(email)) {
                    emailToIndex[email] = emailsCount++;
                    emailToName[email] = name;
                }
            }
        }
        UnionFind uf(emailsCount);
        for (auto& account : accounts) {
            string& firstEmail = account[1];
            int firstIndex = emailToIndex[firstEmail];
            int size = account.size();
            for (int i = 2; i < size; i++) {
                string& nextEmail = account[i];
                int nextIndex = emailToIndex[nextEmail];
                uf.unionSet(firstIndex, nextIndex);
            }
        }
        map<int, vector<string>> indexToEmails;
        for (auto& [email, _] : emailToIndex) {
            int index = uf.find(emailToIndex[email]);
            vector<string>& account = indexToEmails[index];
            account.emplace_back(email);
            indexToEmails[index] = account;
        }
        vector<vector<string>> merged;
        for (auto& [_, emails] : indexToEmails) {
            sort(emails.begin(), emails.end());
            string& name = emailToName[emails[0]];
            vector<string> account;
            account.emplace_back(name);
            for (auto& email : emails) {
                account.emplace_back(email);
            }
            merged.emplace_back(account);
        }
        return merged;
    }
};

C++参考网址

【Java】

/*
Java解法:哈希表+dfs
*/
class Solution {
    public List<List<String>> accountsMerge(List<List<String>> accounts) {
        // 每个账号作为顶点,邮件相同的账号表示顶点相连。问题转换为求存在多少个连通子图。
        HashMap<String, List<Integer>> emalIndex       = new HashMap();
        int aLen    = accounts.size();
        int index   = 0;
        for(List<String> list: accounts){
            String name     = list.get(0);
            for(int i=list.size()-1; i>0; i--){
                List<Integer> idxs  = emalIndex.getOrDefault(list.get(i), new ArrayList());
                idxs.add(index);
                emalIndex.put(list.get(i), idxs);
            }
            index++;
        }
        // 获取相连的顶点
        List<HashSet<Integer>> edges   = new ArrayList();
        for(int i=0; i< aLen; i++){
            List<String> account        = accounts.get(i);
            HashSet<Integer> iset       = new HashSet();
            for(int j=account.size()-1; j> 0; j--){
                String email    = account.get(j);
                if(emalIndex.containsKey(email)){
                    List<Integer> idxs     = emalIndex.get(email);
                    iset.addAll(idxs);
                }
            }
            edges.add(iset);
        }

        // 深度遍历,获取连通的子图。
        List<List<String>> ret   = new ArrayList();
        boolean[] flags     = new boolean[aLen];
        for(int i=0; i< aLen; i++){
            if(!flags[i]){
                HashSet emails     = new HashSet();
                dfs(accounts, emails, edges, flags, i);

                List<String> tmp    = new ArrayList();
                tmp.addAll(emails);
                Collections.sort(tmp);

                String name         = accounts.get(i).get(0);
                tmp.add(0, name);
                ret.add(tmp);
            }
        }

        return ret;

    }

    void dfs(List<List<String>> accounts, HashSet<String> emails, List<HashSet<Integer>> edges , boolean[] flags, int start){  
        List<String> acc    = accounts.get(start);
        emails.addAll(acc.subList(1, acc.size()));
        flags[start]    = true;
        HashSet<Integer> set     = edges.get(start);
        if(set == null || set.size() == 0){
            return;
        }

        for(Integer idx: set){
            if(!flags[idx]){
                dfs(accounts, emails, edges, flags, idx);
            }
        }
    }
}

Java参考网址

【python】

#
#python解法:哈希表+dfs
#
class Solution:
    def build_graph(self,accounts):
        """
        建图
        """
        graph = collections.defaultdict(list)
        for account in accounts:
            master = account[1]
            # 对剩余账户做一个去重
            for email in list(set(account[2:])):
                graph[master].append(email)
                graph[email].append(master)
        
        return graph
    
    def dfs(self,email,graph,visited,emails):
        """
        深搜遍历
        """
        # 已经访问过的就剪枝
        if email in visited:
            return 
        
        visited.add(email)
        emails.append(email)
        
        # 对邻居节点继续深搜
        for neighbor in graph[email]:
            self.dfs(neighbor,graph,visited,emails)
    
    def accountsMerge(self, accounts):
        graph = self.build_graph(accounts)

        res = []
        visited = set()
        for account in accounts:
            emails = []
            self.dfs(account[1],graph,visited,emails)
            if emails:
                res.append([account[0]] + sorted(emails))
        
        return res

python参考网址

你可能感兴趣的:(Leetcode,算法,哈希表,leetcode,图论,dfs)