数据结构 笔记 part2
1. union find 并查集
一种用来解决集合查询和合并的数据结构
并查集能够干什么?
1. find 操作 判断在不在同一个集合中
2. union关于集合合并
例子:
A, B, C的boss 是B D,E,F的boss是E 那么组成了两个集合。
每个节点都包含了一个指针,指向其boss,boss自身指向自己。数据可以用hashmap或者2个数组来表示。
union
老大哥之间的合并,和小弟没关系。
当需要合并的时候,只需要把被合并的部分的boss指针指向合并后的boss。只需要改1个指针。
union模版:
HashMap<Integer, Integer> father = HashMap<Integer, Integer>(); void union( int x, int y) { int fa_x = find(x); int fa_y = find(y); if (fa_x != fa_y) { father.put(fa_x, fa_y); } }
当需要查找的时候,用非递归的方式找到一个节点,其boss为自身的就是该集合的boss。
find 模版:
HashMap<Integer, Integer> father = new HashMap<Integer, Integer>(); int find(int x) { int parent = father.get(x); while(parent != father.get(partent)) { parent = father.get(parent); } return parent; }
完整并查集合模版:
class UnionFind { HashMap<Integer, Integer> father = new HashMap<Integer, Integer>(); UnionFind(){} int find(int x) { int parent = father.get(x); while (parent != father.get(parent)) { parent = father.get(parent); } return parent; } void union (int x, int y) { int fa_x = find(x); int fa_y = find(y); if (fa_x != fa_y) { father.put(fa_x, fa_y); } } }
-------我是分割线---------
Find the Connected Component in the Undirected Graph
Find the number connected component in the undirected graph. Each node in the graph contains a label and a list of its neighbors. (a connected component (or just component) of an undirected graph is a subgraph in which any two vertices are connected to each other by paths, and which is connected to no additional vertices in the supergraph.)
Given graph:
A------B C
\ | |
\ | |
\ | |
\ | |
D E
Return {A,B,D}, {C,E}
. Since there are two connected component which is {A,B,D}, {C,E}
BFS方法:
开辟一个hashmap vistied来存当前遍历的情况,如果是需要返回联通块的数目,那么 value就用integer来表示,如果要返回所有联通块的具体node,那么可以直接用true/false来表示。
/** * Definition for Undirected graph. * class UndirectedGraphNode { * int label; * ArrayList<UndirectedGraphNode> neighbors; * UndirectedGraphNode(int x) { label = x; neighbors = new ArrayList<UndirectedGraphNode>(); } * }; */ public class Solution { /** * @param nodes a array of Undirected graph node * @return a connected set of a Undirected graph */ public List<List<Integer>> connectedSet(ArrayList<UndirectedGraphNode> nodes) { // Write your code here List<List<Integer>> result = new ArrayList<List<Integer>>(); Map<UndirectedGraphNode, Boolean> visited = new HashMap<UndirectedGraphNode, Boolean>(); for (UndirectedGraphNode node : nodes) { visited.put (node, false); } for (UndirectedGraphNode node : nodes){ if (visited.get(node) == false){ bfs(node, visited, result); } } return result; } private static void bfs(UndirectedGraphNode node, Map<UndirectedGraphNode, Boolean> visited, List<List<Integer>> result) { Queue<UndirectedGraphNode> queue = new LinkedList<UndirectedGraphNode>(); List<Integer> row = new ArrayList<>(); visited.put(node, true); queue.offer(node); while (!queue.isEmpty()) { UndirectedGraphNode u = queue.poll(); row.add(u.label); for (UndirectedGraphNode temp : u.neighbors) { if (visited.get(temp) == false) { queue.offer(temp); visited.put(temp, true); } } } Collections.sort(row); result.add(row); } }
并查集方法:
找集合个数的题目,要想到用并查集
/** * Definition for Undirected graph. * class UndirectedGraphNode { * int label; * ArrayList<UndirectedGraphNode> neighbors; * UndirectedGraphNode(int x) { label = x; neighbors = new ArrayList<UndirectedGraphNode>(); } * }; */ public class Solution { /** * @param nodes a array of Undirected graph node * @return a connected set of a Undirected graph */ class UnionFind { HashMap<Integer, Integer> father = new HashMap<Integer, Integer>(); UnionFind(HashSet<Integer> set) { for (Integer temp : set) { father.put(temp, temp); } } int find(int target) { while (father.get(target) != target) { target = father.get(target); } return target; } void union(int x, int y) { int fa_x = find(x); int fa_y = find(y); if (fa_x != fa_y) { father.put(fa_x, fa_y); } } } public List<List<Integer>> generateRes(HashSet<Integer> hashSet, UnionFind uf, int n) { List<List<Integer>> ans = new ArrayList<List<Integer> >(); HashMap<Integer, List<Integer>> hashMap = new HashMap<Integer, List<Integer>>(); for (int i : hashSet) { int fa = uf.find(i); if (!hashMap.containsKey(fa)) { hashMap.put(fa, new ArrayList<Integer>()); } List<Integer> now = hashMap.get(fa); now.add(i); hashMap.put(fa, now); } for (List<Integer> now : hashMap.values()) { Collections.sort(now); ans.add(now); } return ans; } public List<List<Integer>> connectedSet(ArrayList<UndirectedGraphNode> nodes) { // Write your code here HashSet<Integer> hashSet = new HashSet<Integer>(); for (UndirectedGraphNode now : nodes) { hashSet.add(now.label); } UnionFind uf = new UnionFind(hashSet); for (UndirectedGraphNode now : nodes) { for (UndirectedGraphNode neighbour : now.neighbors) { int fnow = uf.find(now.label); int fneighbour = uf.find(neighbour.label); if (fnow != fneighbour) { uf.union(now.label, neighbour.label); } } } return generateRes(hashSet, uf, nodes.size()); } }
由于father指针的链可能会非常长,find 和 union操作都是o(n)的时间复杂度。可以做以下优化,在写并查集find操作的时候,做路径压缩。
public int find(int target){ int parent = target; while(parent != map.get(parent)){ parent = map.get(parent); } int current = target; int next; while (current != map.get(current)) { next = map.get(current); map.put(current, parent); current = next; } return parent; }
--------我是分割线------
联通块: 强联通块 :有向图一个块当中,你找得到我,我可以找不到你。
弱联通块:在有向图一个块当中,你找得到我,我也找得到你。
------我还是分割线----
Number of Islands
Given a 2d grid map of '1'
s (land) and '0'
s (water), count the number of islands. An island is surrounded by water and is formed by connecting adjacent lands horizontally or vertically. You may assume all four edges of the grid are all surrounded by water.
Example 1:
11110
11010
11000
00000
Answer: 1
bfs/dfs都可以做,另外如何存储点,可以新建个point类,或者把二元坐标转化为一元坐标
bfs代码:
public class Solution { public int numIslands(char[][] grid) { if (grid == null || grid.length == 0) { return 0; } int m = grid.length; int n = grid[0].length; int count = 0; boolean[] visited = new boolean[m * n]; for (int i = 0; i < m; i++) { for (int j = 0; j < n; j++) { if (visited[i * n + j] == false && grid[i][j] == '1') { count++; bfs(grid, visited, i * n + j, m , n); } } } return count; } public void bfs(char[][] grid, boolean[] visited, int k, int m, int n){ Queue<Integer> queue = new LinkedList<Integer>(); visited[k] = true; queue.offer(k); while (!queue.isEmpty()) { int current = queue.poll(); if ((current - n) >= 0 && visited[current - n] == false &&grid[current / n - 1][current % n] == '1') { visited[current - n] = true; queue.offer(current - n); } if (current + n < m * n && visited[current + n] == false && grid[current / n + 1][current % n] == '1') { visited[current + n] = true; queue.offer(current + n); } if (current % n - 1 >= 0 && visited[current - 1] == false && grid[current/n][current % n - 1] == '1') { visited[current - 1] = true; queue.offer(current - 1); } if ((current % n + 1) < n && visited[current + 1] == false && grid[current / n][current % n + 1] == '1') { visited[current + 1] = true; queue.offer(current + 1); } } } }
dfs做递归调用,代码简介很多
public class Solution { /** * @param grid a boolean 2D matrix * @return an integer */ private int m, n; public void dfs(boolean[][] grid, int i, int j) { if (i < 0 || i >= m || j < 0 || j >= n) return; if (grid[i][j]) { grid[i][j] = false; dfs(grid, i - 1, j); dfs(grid, i + 1, j); dfs(grid, i, j - 1); dfs(grid, i, j + 1); } } public int numIslands(boolean[][] grid) { // Write your code here m = grid.length; if (m == 0) return 0; n = grid[0].length; if (n == 0) return 0; int ans = 0; for (int i = 0; i < m; i++) { for (int j = 0; j < n; j++) { if (!grid[i][j]) continue; ans++; dfs(grid, i, j); } } return ans; } }
会stack overflow = = 懒得改了
并查集的做法:
我写的比较慢= =
public class Solution { /** * @param grid a boolean 2D matrix * @return an integer */ private boolean[] visited; private int[] offset; public int numIslands(char[][] grid) { if (grid == null || grid.length == 0) { return 0; } // Write your code here int m = grid.length; int n = grid[0].length; int size = m * n; int counter = 0; visited = new boolean[size]; offset = new int[]{-1, 1, n, -n}; UnionFind uf = new UnionFind(size); for (int i = 0; i < size; i++) { if (visited[i]) { continue; } if (grid[i / n][i % n] == '1') { counter++; visited[i] = true; search(i, size, grid, n); } } return counter; } private void search(int index, int total, char[][] grid, int n) { for (int i = 0; i < 4; i++) { if ((index % n == (n - 1) && i == 1) || (index % n == 0 && i == 0)) { continue; } int temp = index + offset[i]; if (temp < 0 || temp >= total) { continue; } if (visited[temp] == false && grid[temp / n][ temp % n] == '1') { visited[temp] = true; search(temp, total, grid, n); } } } class UnionFind { HashMap<Integer, Integer> father = new HashMap<Integer, Integer>(); UnionFind(int total) { for (int i = 0; i < total; i++) { father.put(i, i); } } int find(int target) { int parent = target; while (father.get(parent) != parent) { parent = father.get(parent); } int next; while (father.get(target) != target) { next = father.get(target); father.put(target, parent); target = next; } return parent; } void uniod (int x, int y) { int fa_x = find(x); int fa_y = find(y); if (fa_x != fa_y) { father.put(fa_x, fa_y); } } } }
Follow up
Number of island II
Given a n,m which means the row and column of the 2D matrix and an array of pair A( size k). Originally, the 2D matrix is all 0 which means there is only sea in the matrix. The list pair has k operator and each operator has two integer A[i].x, A[i].y means that you can change the grid matrix[A[i].x][A[i].y] from sea to island. Return how many island are there in the matrix after each operator.
Notice
0 is represented as the sea, 1 is represented as the island. If two 1 is adjacent, we consider them in the same island. We only consider up/down/left/right adjacent.
Given n
= 3
, m
= 3
, array of pair A =[(0,0),(0,1),(2,2),(2,1)]
.
return [1,1,2,2]
.
思路:先建一个每个点的boss都是自身的并查集。每增加一个点,count++, 然后遍历周围,如果能用当前点找到相邻的不同boss的点 count--
代码:
/** * Definition for a point. * class Point { * int x; * int y; * Point() { x = 0; y = 0; } * Point(int a, int b) { x = a; y = b; } * } */ public class Solution { class UnionFind{ HashMap<Integer, Integer> map = new HashMap<>(); UnionFind(int n, int m){ for(int i = 0; i < n; i++){ for(int j = 0; j < m; j++){ int id = convertId(i, j , m); map.put(id, id); } } } public int find(int target){ while(target != map.get(target)){ target = map.get(target); } return target; } public void union(int a, int b){ int aF = find(a); int bF = find(b); if(aF != bF){ map.put(aF, bF); } } } public int convertId(int i, int j, int m){ return i * m + j; } public List<Integer> numIslands2(int n, int m, Point[] operators) { // Write your code here if(operators == null || operators.length == 0){ return new ArrayList<Integer>(); } List<Integer> result = new ArrayList<>(); UnionFind uf = new UnionFind(n, m); int[][] island = new int[n][m]; int[] dx = new int[]{0, 1, 0, -1}; int[] dy = new int[]{1, 0, -1, 0}; int count = 0; for(int i = 0; i < operators.length; i ++){ int x = operators[i].x; int y = operators[i].y; int id = convertId(x, y, m); if(island[x][y] == 0){ island[x][y] = 1; count ++; for(int j = 0; j < 4; j++){ int nx = x + dx[j]; int ny = y + dy[j]; int nid = convertId(nx, ny, m); if(nx >= 0 && nx < n && ny >= 0 && ny < m && island[nx][ny] == 1){ if(uf.find(id) != uf.find(nid)){ uf.union(id, nid); count --; } } } } result.add(count); } return result; } }
-----我是分割线-------
Given n
nodes labeled from 0
to n - 1
and a list of undirected
edges (each edge is a pair of nodes), write a function to check whether these edges make up a valid tree.
Given n = 5
and edges = [[0, 1], [0, 2], [0, 3], [1, 4]]
, return true.
Given n = 5
and edges = [[0, 1], [1, 2], [2, 3], [1, 3], [1, 4]]
, return false.
思路
判断输入的边是否能构成一个树,我们需要确定两件事:
-
这些边是否构成环路,如果有环则不能构成树 (做union的时候,如果这两个点已经属于一个集合了 说明有环路)
-
这些边是否能将所有节点连通,如果有不能连通的节点则不能构成树 (所有的点属于1个集合)
public class Solution { class UnionFind{ HashMap<Integer, Integer> father = new HashMap<Integer, Integer>(); UnionFind(int n) { for (int i = 0; i < n; i++) { father.put(i, i); } } int find(int target) { int parent = target; while (father.get(parent) != parent) { parent = father.get(parent); } int next; while (father.get(target) != target) { next = father.get(target); father.put(target, parent); target = next; } return parent; } boolean union(int x, int y) { int fa_x = find(x); int fa_y = find(y); if (fa_x == fa_y) { return false; } else { father.put(fa_x, fa_y); return true; } } } /** * @param n an integer * @param edges a list of undirected edges * @return true if it's a valid tree, or false */ public boolean validTree(int n, int[][] edges) { if (n == 0) { return false; } UnionFind uf = new UnionFind(n); int len = edges.length; for (int i = 0; i < len; i++) { if(uf.union(edges[i][0], edges[i][1]) == false) { return false; } } int current = uf.find(0); for (int i = 1; i < n; i++) { if (current != uf.find(i)) { return false; } } return true; } }