721. 账户合并

给定一个列表 accounts,每个元素 accounts[i] 是一个字符串列表,其中第一个元素 accounts[i][0] 是 名称 (name),其余元素是 emails 表示该帐户的邮箱地址。

现在,我们想合并这些帐户。如果两个帐户都有一些共同的邮件地址,则两个帐户必定属于同一个人。请注意,即使两个帐户具有相同的名称,它们也可能属于不同的人,因为人们可能具有相同的名称。一个人最初可以拥有任意数量的帐户,但其所有帐户都具有相同的名称。

合并帐户后,按以下格式返回帐户:每个帐户的第一个元素是名称,其余元素是按顺序排列的邮箱地址。accounts 本身可以以任意顺序返回。

例子 1:

Input: 
accounts = [["John", "johnsmith@mail.com", "john00@mail.com"], ["John", "johnnybravo@mail.com"], ["John", "johnsmith@mail.com", "john_newyork@mail.com"], ["Mary", "mary@mail.com"]]
Output: [["John", 'john00@mail.com', 'john_newyork@mail.com', 'johnsmith@mail.com'],  ["John", "johnnybravo@mail.com"], ["Mary", "mary@mail.com"]]
Explanation: 
  第一个和第三个 John 是同一个人,因为他们有共同的电子邮件 "johnsmith@mail.com"。 
  第二个 John 和 Mary 是不同的人,因为他们的电子邮件地址没有被其他帐户使用。
  我们可以以任何顺序返回这些列表,例如答案[['Mary','mary@mail.com'],['John','johnnybravo@mail.com'],
  ['John','john00@mail.com','john_newyork@mail.com','johnsmith@mail.com']]仍然会被接受。

注意:

  • accounts的长度将在[1,1000]的范围内。
  • accounts[i]的长度将在[1,10]的范围内。
  • accounts[i][j]的长度将在[1,30]的范围内。
### C语言实现账户合并的数据结构设计与示例代码 #### 设计思路 为了有效地处理账户合并问题,在C语言中可以采用并查集(Disjoint Set Union, DSU)数据结构来管理不同用户的邮箱地址集合。通过构建哈希表存储每个唯一电子邮件对应的用户ID,再利用并查集追踪这些ID之间的连接关系。 #### 数据结构定义 首先定义`HashNode`和`HashMap`用于表示哈希表节点及其整体结构[^1]: ```c typedef struct HashNode { char* key; int value; struct HashNode* next; } HashNode; typedef struct { int size; HashNode** table; } HashMap; ``` 接着创建并查集所需的辅助函数以及初始化过程: ```c // 初始化并查集 void initUnionFind(UnionFind *uf, int n) { uf->parent = malloc(n * sizeof(int)); uf->rank = malloc(n * sizeof(int)); for (int i = 0; i < n; ++i) { uf->parent[i] = i; uf->rank[i] = 0; } } ``` 针对具体的应用场景——即账户合并功能,则需进一步扩展上述基础组件的功能。这里假设输入是一系列由用户名与其关联的一组或多组电子信箱构成的列表;目标是要找出哪些账号实际上属于同一个实体,并最终返回按字母顺序排列后的结果。 为此引入一个新的结构体Account用来保存单个账户的相关信息: ```c typedef struct Account { char name[50]; char **emails; int emailCount; } Account; ``` 同时还需要编写几个重要的算法模块完成整个流程: - `find`: 查找某个元素所属集合的代表元; - `unionSets`: 将两个不同的子集合并成一个更大的集合; - `addEmailToMap`: 把新发现的邮件加入到映射表里去; - `mergeAccounts`: 对所有已知联系进行综合分析得出最后结论。 下面是完整的源码片段展示如何运用以上提到的技术手段解决实际问题: ```c #include <stdio.h> #include <stdlib.h> #include <string.h> #define HASH_SIZE 1009 /* 哈希表初始容量 */ /* ... 上述已经给出的部分类型声明省略 ... */ static inline unsigned long hash(const char *str); unsigned long hash(const char *str){ unsigned long h=0,g; while(*str != '\0'){ h=(h<<4)+*str++; g=h&0xf0000000L; if(g!=0){h^=g>>24;} h&=~g; } return h%HASH_SIZE; } // 并查集查找操作 int find(UnionFind *uf, int x) { if (uf->parent[x] != x) { uf->parent[x] = find(uf, uf->parent[x]); } return uf->parent[x]; } // 连接两棵不同的树 void unionSets(UnionFind *uf, int x, int y) { int rootX = find(uf, x); int rootY = find(uf, y); if (rootX != rootY) { if (uf->rank[rootX] > uf->rank[rootY]) { uf->parent[rootY] = rootX; } else if (uf->rank[rootX] < uf->rank[rootY]) { uf->parent[rootX] = rootY; } else { uf->parent[rootY] = rootX; uf->rank[rootX]++; } } } // 添加新的email至hashmap中 void addEmailToMap(char ***accountEmails, int accountSize, int *accountEmailSizes, const char *emailStr, int accountId, HashMap *emailIdMap, UnionFind *uf) { // 如果该email已经在其他地方出现过,则将其对应id相连 int id = -1; int pos = hash(emailStr)%HASH_SIZE; HashNode *node = emailIdMap->table[pos]; while(node && strcmp(node->key,emailStr)!=0){ node=node->next; } if(!node){ node=calloc(sizeof(struct HashNode),1); node->key=strdup(emailStr); node->value=emailIdMap->size; node->next=emailIdMap->table[pos]; emailIdMap->table[pos]=node; emailIdMap->size++; (*accountEmailSizes)[accountId]++; accountEmails[accountId][(*accountEmailSizes)[accountId]-1]=(char*)malloc(strlen(emailStr)*sizeof(char)+1); strcpy(accountEmails[accountId][(*accountEmailSizes)[accountId]-1],emailStr); id=emailIdMap->size-1; }else{ id=find(uf,node->value); unionSets(uf,id,accountId); } } // 主要逻辑:遍历accounts数组并将相同email归属同一分组 void mergeAccounts(Account accounts[], int numAccounts, HashMap *emailIdMap, UnionFind *uf) { for (int i = 0; i < numAccounts; ++i) { for (int j = 1; j < accounts[i].emailCount; ++j) { // Skip first element which is the username addEmailToMap(&accounts[i].emails,&numAccounts, &(accounts[i].emailCount), accounts[i].emails[j], i, emailIdMap, uf); } } } // 输出最终结果 void printResult(Account mergedAccounts[], int resultSize) { for (int i = 0; i < resultSize; ++i) { qsort(mergedAccounts[i].emails + 1, mergedAccounts[i].emailCount - 1,sizeof(char*),strcmp
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值