Skip to content

Commit 4c74687

Browse files
committed
feat: 🎸 leetcode 721
1 parent 6eb4630 commit 4c74687

File tree

2 files changed

+120
-0
lines changed

2 files changed

+120
-0
lines changed

src/0001-1000/721/accountsMerge.ts

+67
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
// <Recursion, DFS, HashTable>
2+
// Time: O(nlogn)
3+
// Space: O(n)
4+
5+
function dfs(
6+
accounts: string[][],
7+
emailSet: Set<string>,
8+
emailToIndexes: Map<string, number[]>,
9+
visited: boolean[],
10+
index: number,
11+
) {
12+
visited[index] = true
13+
14+
for (let i = 1; i < accounts[index].length; ++i) {
15+
const email = accounts[index][i]
16+
17+
if (emailSet.has(email)) {
18+
continue
19+
}
20+
21+
emailSet.add(email)
22+
23+
for (const j of emailToIndexes.get(email)!) {
24+
if (!visited[j]) {
25+
dfs(accounts, emailSet, emailToIndexes, visited, j)
26+
}
27+
}
28+
}
29+
}
30+
31+
function accountsMerge(accounts: string[][]): string[][] {
32+
// edge cases
33+
if (!accounts.length) {
34+
return []
35+
}
36+
37+
const n = accounts.length
38+
const emailToIndexes = new Map<string, number[]>() // Map<email, index[]>
39+
40+
for (let i = 0; i < n; ++i) {
41+
for (let j = 1; j < accounts[i].length; ++j) {
42+
const email = accounts[i][j]
43+
44+
if (!emailToIndexes.has(email)) {
45+
emailToIndexes.set(email, [])
46+
}
47+
48+
emailToIndexes.get(email)!.push(i)
49+
}
50+
}
51+
52+
const visited: boolean[] = new Array(n)
53+
const emailSet = new Set<string>() // Set<email>
54+
const mergedAccounts: string[][] = []
55+
56+
for (let i = 0; i < n; ++i) {
57+
if (!visited[i]) {
58+
emailSet.clear()
59+
dfs(accounts, emailSet, emailToIndexes, visited, i)
60+
mergedAccounts.push([accounts[i][0], ...Array.from(emailSet).sort()])
61+
}
62+
}
63+
64+
return mergedAccounts
65+
}
66+
67+
export { accountsMerge }

src/0001-1000/721/accountsMerge2.ts

+53
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
import { UnionFindWithRank } from 'classes/UnionFindWithRank'
2+
3+
// <Union Find, HashTable>
4+
// Time: O(nlogn * α(n)), α() is the inverse Ackermann function
5+
// Space: O(n)
6+
7+
function accountsMerge(accounts: string[][]): string[][] {
8+
// edge cases
9+
if (!accounts.length) {
10+
return []
11+
}
12+
13+
const n = accounts.length
14+
const uf = new UnionFindWithRank(n)
15+
const emailToIndex = new Map<string, number>() // Map<email, index>
16+
17+
for (let i = 0; i < n; ++i) {
18+
for (let j = 1; j < accounts[i].length; ++j) {
19+
const email = accounts[i][j]
20+
21+
if (!emailToIndex.has(email)) {
22+
emailToIndex.set(email, i)
23+
continue
24+
}
25+
26+
uf.union(i, emailToIndex.get(email)!)
27+
}
28+
}
29+
30+
const indexToEmails = new Map<number, Set<string>>() // Map<index, Set<email>>
31+
32+
for (let i = 0; i < n; ++i) {
33+
const root = uf.find(i)
34+
35+
if (!indexToEmails.has(root)) {
36+
indexToEmails.set(root, new Set<string>())
37+
}
38+
39+
for (let j = 1; j < accounts[i].length; ++j) {
40+
indexToEmails.get(root)!.add(accounts[i][j])
41+
}
42+
}
43+
44+
const mergedAccounts: string[][] = []
45+
46+
for (const [index, emails] of indexToEmails) {
47+
mergedAccounts.push([accounts[index][0], ...Array.from(emails).sort()])
48+
}
49+
50+
return mergedAccounts
51+
}
52+
53+
export { accountsMerge }

0 commit comments

Comments
 (0)