The stones on the same col and same row is an island, therefore actually this is a number of islands problem. The total number of stones minus the "number of islands" equals to the number of stones to remove. Therefore the problem becomes to find all connected number of islands. This can be achieved by DFS (O(n^2)) or UnionFind (O(n)).
399. Evaluate Division
问a / b = 2.0, b / c = 3.0.
queries are: a / c = ?, b / a = ?, a / e = ?, a / a = ?, x / x = ? .
Two cases to find redundant connection for directed graph:
A node has more than one parents
There is a loop
Therefore 2 candidates are needed. Can1 represents case1, can2 represents case2, else union. If both issues present, then the answer should be the first edge which results in "multiple parents" issue.
We'll also include dsu.top, which tells us the size of the "roof", or the component connected to the top edge. We use an ephemeral "source" node with label M * N where all nodes on the top edge (with row number 0) are connected to the source node.
public int countComponents(int n, int[][] edges) {
Map<Integer, Set<Integer>> map = new HashMap<>();
for (int[] edge : edges) {
map.putIfAbsent(edge[0], new HashSet<>());
map.putIfAbsent(edge[1], new HashSet<>());
map.get(edge[0]).add(edge[1]);
map.get(edge[1]).add(edge[0]);
}
Set<Integer> visit = new HashSet<>();
int count = 0;
for (int i = 0; i < n ; i++) {
if (visit.contains(i)) continue;
Queue<Integer> q = new LinkedList<>();
q.offer(i);
visit.add(i);
while (!q.isEmpty()) {
int curt = q.poll();
if (!map.containsKey(curt)) continue;
for (int next : map.get(curt)) {
if (visit.contains(next)) continue;
visit.add(next);
q.offer(next);
}
}
count++;
}
return count;
}
def countComponents(self, n: int, edges: List[List[int]]) -> int:
roots = [i for i in range(n)]
for edge in edges:
# find
rootU, rootV = self.find(edge[0], roots), self.find(edge[1], roots)
# UNION
if rootU != rootV:
roots[rootU] = roots[rootV]
n -= 1
return n
def find(self, p, roots):
while p != roots[p]:
roots[p] = roots[roots[p]]
p = roots[p]
return p
public int findCircleNum(int[][] M) {
int n = M.length;
int[] roots = new int[n];
for (int i = 0; i < n; i++) roots[i] = i;
int res = n;
for (int i = 0; i < n; i++) {
for (int j = i + 1; j < n; j++) {
int rootI = find(roots, i);
int rootJ = find(roots, j);
if (M[i][j] == 1 && rootI != rootJ) {
roots[rootI] = roots[rootJ];
res--;
}
}
}
return res;
}
private int find(int[] roots, int p) {
while (p != roots[p]) {
roots[p] = roots[roots[p]];
p = roots[p];
}
return roots[p];
}
}
def findCircleNum(self, M: List[List[int]]) -> int:
n = len(M)
roots = [i for i in range(n)]
res = n
for i in range(n):
for j in range(i + 1, n):
rootU, rootV = self.find(i, roots), self.find(j, roots)
if M[i][j] == 1:
if rootU != rootV:
roots[rootU] = roots[rootV]
res -= 1
return res
def find(self, p, roots):
while p != roots[p]:
roots[p] = roots[roots[p]]
p = roots[p]
return p
public boolean validTree(int n, int[][] edges) {
if (edges == null) return false;
int[] roots = new int[n];
for (int i = 0; i < roots.length; i++) roots[i] = i;
for (int[] edge : edges) {
int rootP = find(edge[0], roots);
int rootQ = find(edge[1], roots);
if (rootP == rootQ) return false;
roots[rootP] = roots[rootQ];
}
return edges.length == n - 1;
}
private int find(int p, int[] roots) {
while (roots[p] != p) {
roots[p] = roots[roots[p]];
p = roots[p];
}
return roots[p];
}
public int removeStones(int[][] stones) {
Set<int[]> visited = new HashSet<>();
int numOfIslands = 0;
for (int[] s : stones) {
if (!visited.contains(s)) {
dfs(s, visited, stones);
numOfIslands++;
}
}
return stones.length - numOfIslands;
}
private void dfs(int[] s, Set<int[]> visited, int[][] stones) {
visited.add(s);
for (int[] nextS : stones) {
if (!visited.contains(nextS) && (s[0] == nextS[0] || s[1] == nextS[1])) {
dfs(nextS, visited, stones);
}
}
}
public int removeStones(int[][] stones) {
int numOfIslands = 0;
int[] roots = new int[200000];
for (int i = 0; i < roots.length; i++) {
roots[i] = i;
}
for (int[] s : stones) {
union(s[0], 100000 + s[1], roots);
}
Set<Integer> rootsSet = new HashSet<>();
for (int[] s : stones) {
int root = find(s[0], roots);
rootsSet.add(root);
}
return stones.length - rootsSet.size();
}
private void union(int A, int B, int[] roots) {
int rootA = find(A, roots);
int rootB = find(B, roots);
roots[rootA] = roots[rootB];
}
private int find(int p, int[] roots) {
while (p != roots[p]) {
roots[p] = roots[roots[p]];
p = roots[p];
}
return p;
}
public int[] findRedundantConnection(int[][] edges) {
int n = edges.length;
int[] root = new int[n + 1];
for (int i = 0; i <= n; i++) root[i] = i;
for (int[] edge : edges) {
int rootp = find(edge[0], root);
int rootq = find(edge[1], root);
if (rootp == rootq) return edge;
root[rootp] = root[rootq];
}
return new int[]{-1, -1};
}
private int find(int p, int[] root) {
while (p != root[p]) {
root[p] = root[root[p]];
p = root[p];
}
return root[p];
}
int[] can1 = null;
int[] can2 = null;
for (int[] edge : edges) {
int rootU = find(edge[0] - 1, roots);
int rootV = find(edge[1] - 1, roots);
if (rootU != rootV) {
if (rootV != edge[1] - 1) {
can1 = edge;
} else {
roots[rootV] = rootU;
}
} else {
can2 = edge;
}
}
if (can1 == null) return can2;
if (can2 == null) return can1;
for (int[] edge : edges) {
if (edge[1] == can1[1]) return edge;
}
public List<List<String>> accountsMerge(List<List<String>> accounts) {
Map<String, Integer> rootmap = new HashMap<>();
UnionFind uf = new UnionFind(accounts.size());
for (int i = 0; i < accounts.size(); i++) {
for (int j = 1; j < accounts.get(i).size(); j++) {
String email = accounts.get(i).get(j);
if (rootmap.containsKey(email)) {
uf.union(i, rootmap.get(email));
} else {
rootmap.put(email, i);
}
}
}
Map<Integer, Set<String>> map = new HashMap<>(); // index : email1 , email2
for (int i = 0; i < accounts.size(); i++) {
for (int j = 1; j < accounts.get(i).size(); j++) {
int rootI = uf.find(i);
map.putIfAbsent(rootI, new HashSet<>());
map.get(rootI).add(accounts.get(i).get(j));
}
}
List<List<String>> res = new ArrayList<>();
for (int key : map.keySet()) {
List<String> curt = new ArrayList<>(map.get(key));
Collections.sort(curt);
curt.add(0, accounts.get(key).get(0));
res.add(curt);
}
return res;
}
class UnionFind {
int[] roots;
UnionFind(int N) {
roots = new int[N];
for (int i = 0; i < N; i++) roots[i] = i;
}
int find(int p) {
while (p != roots[p]) {
roots[p] = roots[roots[p]];
p = roots[p];
}
return roots[p];
}
void union(int p, int q) {
int rootP = find(p);
int rootQ = find(q);
if (rootP != rootQ) roots[rootP] = roots[rootQ];
}
}
public String smallestEquivalentString(String A, String B, String S) {
int[] graph = new int[26];
for(int i = 0; i < 26; i++) {
graph[i] = i;
}
for(int i = 0; i < A.length(); i++) {
int a = A.charAt(i) - 'a';
int b = B.charAt(i) - 'a';
int end1 = find(graph, b);
int end2 = find(graph, a);
if(end1 < end2) {
graph[end2] = end1;
} else {
graph[end1] = end2;
}
}
StringBuilder sb = new StringBuilder();
for(int i = 0; i < S.length(); i++) {
char c = S.charAt(i);
sb.append((char)('a' + find(graph, c - 'a')));
}
return sb.toString();
}
private int find(int[] graph, int idx) {
while(graph[idx] != idx) {
idx = graph[idx];
}
return idx;
}
Iterate through all nodes, union i, j where node == 1
Initialize ‘count’ array to store root of each malware nodes, count[root]++
Iterate through initial, find root of each one, if count[root] == 1, refresh ans and ansSize by condition highlighted. RootSize > ansSize && rootSize == ansSize && node < ans
If ans = -1, assign ans as min of all nodes
int[] count = new int[N];
for (int node : initial) {
count[uf.find(node)]++;
}
int ans = -1, ansSize = -1;
for (int node : initial) {
int root = uf.find(node);
if (count[root] == 1) {
int rootSize = uf.size(root);
if (rootSize > ansSize || (rootSize == ansSize && node < ans)) {
ansSize = rootSize;
ans = node;
}
}
}
Union relation between i and j, use uf.union(stone[0], stone[1] + 10000), since
0 <= stones[i][j] < 10000.
Set a set and put in all roots of i. Return N - seen.size();
UF solution2:
UnionFind uf = new UnionFind(stones);
int total = stones.length;
for(int[] stone: stones) {
uf.union(stone[0], stone[1] + 10000);
}
return total - uf.getCount();
UF {
for(int[] stone: stones) {
int i = stone[0], j = stone[1];
if(parents[i] == -1) count++;
parents[i] = i;
if(parents[j + 10000] == -1) count++;
parents[j + 10000] = j + 10000;
}
}
If input is ‘ ’, we do nothing;
If input is ‘/’, we connect lower left vertex with upper right vertex;
If input is ‘\’, we connect upper left vertex with lower right vertex.
for (int row = 0; row < n; row++) {
for (int col = 0; col < n; col++) {
char op = grid[row].charAt(col);
if (op == '/') {
if (uf.isConnected(row, col + 1, row + 1, col))
cnt += 1;
uf.union(row, col + 1, row + 1, col);
} else if (op == '\\') {
if (uf.isConnected(row, col, row + 1, col + 1))
cnt += 1;
uf.union(row, col, row + 1, col + 1);
}
}
}
int[] uf = new int[26];
public boolean equationsPossible(String[] equations) {
for (int i = 0; i < 26; ++i) uf[i] = i;
for (String e : equations)
if (e.charAt(1) == '=')
uf[find(e.charAt(0) - 'a')] = find(e.charAt(3) - 'a');
for (String e : equations)
if (e.charAt(1) == '!' && find(e.charAt(0) - 'a') == find(e.charAt(3) - 'a'))
return false;
return true;
}
public int find(int x) {
if (x != uf[x]) uf[x] = find(uf[x]);
return uf[x];
}