并查集
关于路径压缩
//隔代压缩 int find(int x) { while (x != parent[x]) { x = parent[x]; parent[x] = parent[parent[x]]; } return parent[x]; } //完全压缩,一般用于带权图,因为要遍历边 int find(int x) { if (x != parent[x]) { //int mark = parent[x]; parent[x] = find[parent[x]]; //weight[x] += weight[mark]; } return parent[x]; //注意这里递归的是parent[x],x的没有变的 }
朋友圈 题目 解析
class Solution { class UF { private int count; private int parent[]; //它的父节点 private int size[]; //以自己为根节点的树有多少节点 public UF(int n) { this.count = n; parent = new int[n]; size = new int[n]; //初始化的时候自己的父节点指向自己 for (int i = 0; i < n; i++) { parent[i] = i; size[i] = 1; } } //找到x的根节点 private int find(int x) { while (x != parent[x]) { //路径压缩,画个图就明白了。就是和自己的父亲做兄弟。 parent[x] = parent[parent[x]]; x = parent[x]; } return x; } //连接 public void union(int p, int q) { //找到各自的根节点。 int rootP = find(p); //你看,每次find的时候都进行了路径压缩。 int rootQ = find(q); if (rootP == rootQ) return; //把小的树接到大的树上面 if (size[rootQ] > size[rootP]) { parent[rootP] = rootQ; size[rootQ] += size[rootP]; } else { parent[rootQ] = rootP; size[rootP] += size[rootQ]; } count--; } public boolean connect(int p, int q) { return find(p) == find(q); } public int count() { return count; } } public int findCircleNum(int[][] M) { int N = M.length; UF uf = new UF(N); for (int i = 0; i < N; i++) { for (int j = 0; j < N; j++) { if (M[i][j] == 1) { uf.union(i, j); } } } return uf.count(); } }
深度优先
class Solution { public int findCircleNum(int[][] M) { int N = M.length, count = 0; boolean[] V = new boolean[N]; for (int i = 0; i < N; i++) { if (!V[i]) { count++; dfs(V, M, i); } } return count; } private void dfs(boolean[] V, int[][]M, int i) { for (int j = 0; j < M.length; j++) { if (M[i][j] == 1 && !V[j]) { V[j] = true; dfs(V, M, j); } } } }
广度优先
class Solution { public int findCircleNum(int[][] M) { int N = M.length, count = 0; Queue<Integer> queue = new LinkedList<>(); boolean[] V = new boolean[N]; for (int i = 0; i < N; i++) { if (!V[i]) { queue.offer(i); count++; while (!queue.isEmpty()){ int k = queue.poll(); V[k] = true; for (int j = 0; j < N; j++) { if (M[k][j] == 1 && !V[j]) { queue.offer(j); } } } } } return count; } }
模板一放,直接ac
class Solution { class UF { private int[] parent; private int[] size; UF(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 find(int x) { while (x != parent[x]) { parent[x] = parent[parent[x]]; x = parent[x]; } return x; } public void union(int p, int q) { int rp = find(p), rq = find(q); if (p == q) return; if (size[rp] > size[rq]) { parent[rq] = rp; size[rp] += size[rq]; } else { parent[rp] = rq; size[rq] += size[rp]; } } public int size(int x) { return size[x]; } } public int minMalwareSpread(int[][] graph, int[] initial) { int n = graph.length; UF uf = new UF(n); for (int i = 0; i < n; i++) { for (int j = i + 1; j < n; j++) { if (graph[i][j] == 1) { uf.union(i, j); } } } int[] count = new int[n]; for (int x : initial) { count[uf.find(x)]++; } int res = -1, max = 0; for (int x : initial) { int rx = uf.find(x); if (count[rx] == 1) { int sx = uf.size(rx); if (sx > max) { max = sx; res = x; } else if (sx == max && x < res) { res = x; } } } if (res == -1) { res = Integer.MAX_VALUE; for (int x : initial) { res = Math.min(res, x); } } return res; } }
尽量减少恶意软件的传播
先用并查集构造出树。如果一棵树里只有一个坏节点,那么这个节点可以去掉。所以找出只有一个坏节点并且节点数量最多的树。
class Solution { class UF { private int[] parent; private int[] size; UF(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 find(int x) { while (x != parent[x]) { parent[x] = parent[parent[x]]; x = parent[x]; } return x; } public void union(int p, int q) { int rp = find(p), rq = find(q); if (p == q) return; if (size[rp] > size[rq]) { parent[rq] = rp; size[rp] += size[rq]; } else { parent[rp] = rq; size[rq] += size[rp]; } } public int size(int x) { return size[x]; } } public int minMalwareSpread(int[][] graph, int[] initial) { int n = graph.length; UF uf = new UF(n); for (int i = 0; i < n; i++) { for (int j = i + 1; j < n; j++) { if (graph[i][j] == 1) { uf.union(i, j); } } } int[] count = new int[n]; for (int x : initial) { count[uf.find(x)]++; } int res = -1, max = 0; for (int x : initial) { int rx = uf.find(x); if (count[rx] == 1) { int sx = uf.size(rx); if (sx > max) { max = sx; res = x; } else if (sx == max && x < res) { res = x; } } } if (res == -1) { res = Integer.MAX_VALUE; for (int x : initial) { res = Math.min(res, x); } } return res; } }
除法求值
带权并查集
class Solution { public double[] calcEquation(List<List<String>> equations, double[] values, List<List<String>> queries) { //字符串到数字的映射,这样就可以放在数组里了 Map<String, Integer>map = new HashMap<>(); double[] res = new double[queries.size()]; UF uf = new UF(2 * values.length); int id = 0; for (int i = 0; i < values.length; i++) { String a = equations.get(i).get(0); String b = equations.get(i).get(1); if (!map.containsKey(a)) { map.put(a, id++); } if (!map.containsKey(b)) { map.put(b, id++); } uf.union(map.get(a), map.get(b), values[i]); //uf.union(map.get(b), map.get(a), 1 / values[i]); //并不用反向再连一遍,因为并查集里面已经连接了a和b。在uf.get里得到正确的结果。 } for (int i = 0; i < queries.size(); i++) { String a = queries.get(i).get(0); String b = queries.get(i).get(1); if (!map.containsKey(a) || !map.containsKey(b)) { res[i] = -1.0; }else { res[i] = uf.get(map.get(a), map.get(b)); } } return res; } class UF { private int[] parent; //父节点 private double[] weight; //带权,指该节点到其父节点的权值 public UF(int n) { parent = new int[n]; weight = new double[n]; for (int i = 0; i < n; i++) { parent[i] = i; weight[i] = 1.0; } } private int find(int x) { //递归是parent[x], x还是那个x if (x != parent[x]) { int mark = parent[x]; parent[x] = find(parent[x]); weight[x] *= weight[mark]; } return parent[x]; } public void union(int a, int b, double value) { int rootA = find(a); int rootB = find(b); if (rootA == rootB) return; parent[rootA] = rootB; weight[rootA] = value * weight[b] / weight[a]; } public double get(int a, int b) { if (find(a) != find(b)) return -1.0; return weight[a] / weight[b]; } } }