And check the collection: Wulin sect chaos
There are some advanced data structures in the data structure, but most of them are not difficult to implement. This paper will go through and look up the set.
The parallel query set is used to process the query and merged data structure of disjoint sets. There is only one core statement. Here is a story to show you the implementation and use of the parallel query set.
1, A story thoroughly understood and collected: the battle of Wulin
There are n sects in Wulin. We use the array sects [] to indicate who is the boss of a sect
int[] sects = new int[n + 1]; // It can also be from 0 to n-1, according to the meaning and preference of the topic
All sects do not interfere with each other and are still in the stage of peaceful development. At this time, all sects think they are the boss.
for (int i = 1; i <= n; i++) sects[i] = i; // I'm the boss
Then the struggle between the various sects began.
If two sects meet, it must be a big war. Therefore, before the war, each sect should find its boss through the find() function to help. (at this time, there is no merger between sects, so the boss found is himself)
public int find(int x) { return sects[x]; }
After finding the boss, the two sects a and b officially fought on the battlefield
int bossA = find(a); int bossB = find(b);
In the fight between them, we assume that bossA wins. At this time, BOSB will be annexed by boosA, and the boss of BOSB will become bossA. (assuming bossB wins)
int bossA = find(a); int bossB = find(b); sects[bossB] = boosA;
However, there are many sects in the Wulin, so it is a big fight. Each sect is crazy to annex. Therefore, sometimes two sects find the same boss, so the annexation occurs only when the boss is not the same person.
Here we complete the union() method
public void union(int a, int b) { int bossA = find(a); int bossB = find(b); if (boosA != bossB) sects[bossB] = boosA; }
After a period of chaos, it became very difficult to find the boss. Sect z found his boss y, but unexpectedly, y was once swallowed by X. y has his boss x, and X may have his boss... So finding the boss becomes a manual job.
Only when the final boss (assuming m) is found, the boss who has not lost, that is, m who thinks he is the boss, will return
public int find(int x) { if (sects[x] == x) return sects[x]; return find(sects[x]); }
However, in the battlefield, we can't let looking for the boss waste time, so in order to find the final boss as soon as possible, the younger brothers reached a consensus: if z's boss is y and Y's boss is x, then z should go directly to x, that is, "path compression": directly let z's boss point to Y's boss
public int find(int x) { if (sects[x] == x) return sects[x]; return sects[x] = find(sects[x]); // Path compression }
This is the core method of the union search set, find(). If it is represented by a statement:
public int find(int x) { return x == sects[x] ? x : (sects[x] = find(sects[x])); }
Finally, after the bloody storm in Wulin, some sects merged into a large sect, while others did not.
Here, the story is over. We get sects [], which can be used to judge whether the two sects belong to the same boss.
2, Main methods of joint search set
If you understand the above story, the method is very easy to write, and you can change it freely to achieve the desired effect.
Core statement of parallel query set: find()
public int find(int x) { return x == sects[x] ? x : (sects[x] = find(sects[x])); }
Parallel query set initialization:
int[] sects = new int[n + 1]; for (int i = 1; i <= n; i++) sects[i] = i;
Consolidation method of joint query set: union()
public void union(int a, int b) { int bossA = find(a); int bossB = find(b); if (boosA != bossB) sects[bossB] = boosA; }
union() is not dead. Is if (boosa! = bossb) redundant?
If the question asks to judge whether the two sects already belong to the same sect:
public boolean union(int a, int b) { int bossA = find(a); int bossB = find(b); if (boosA == bossB) return true; sects[bossB] = boosA; return false; }
Whether sections [bossb] = boosa or sections [Bossa] = boosb is also important. Readers can study it by themselves
3, And check the actual combat
1,LeetCode 547 number of provinces
First create and query the set, then write the find() method, and then traverse the merge.
The question is how to get the answer?
In fact, the answer is what we call the final number of bosses. If a city is not annexed, it will be the boss of a collection, and a collection has only one boss (because sects[bossB] = boosA), so you can get the number of provinces with sects[i] == i
int[] provinces; public int findCircleNum(int[][] isConnected) { int len = isConnected.length; int ans = 0; provinces = new int[len]; // Joint search set for (int i = 0; i < len; i++) provinces[i] = i; for (int i = 0; i < len; i++) for (int j = i + 1; j < len; j++) if (isConnected[i][j] == 1) { int a = find(i); int b = find(j); if (a != b) provinces[b] = a; } for (int i = 0; i < len; i++) if (provinces[i] == i) ans++; return ans; } private int find(int x) { if (x== provinces[x]) return x; return provinces[x] = find(provinces[x]); }
2,LeetCode 684 redundant connection
In the same way of thinking, create and query the set, write out the find() method, and if it is found that it already belongs to the same intersection set during merging, record it
int[] fa; public int[] findRedundantConnection(int[][] edges) { fa = new int[edges.length + 1]; for (int i = 1; i <= edges.length; i++) fa[i] = i; int[] ans = new int[2]; for (int[] cur : edges) { int a = find(cur[0]); int b = find(cur[1]); if (a != b) fa[a] = b; else ans = cur; } return ans; } private int find(int index) { return index == fa[index] ? index : (fa[index] = find(fa[index])); }
3,Area surrounded by LeetCode 130
The joint search set is not the optimal solution, but the solution of the joint search set is very ingenious, which can expand ideas for the use of the joint search set
The first step is still to create and query sets and write out the find() method. The creation of parallel query sets here can use a one-dimensional array
fa = new int[rows * cols + 1];
The function getIndex is used to convert the two-dimensional subscript into one-dimensional subscript
private int getIndex(int i, int j) { return i * cols + j; }
The areas connected to the boundary will be merged into the same set, which is why the length should be + 1
final outer = rows * cols; fa[outer] = outer; // A collection of areas adjacent to the boundary if (adjoinTheBound(x, y)) { // If the current x, y is adjacent to the boundary union(getIndex(x, y), outer); }
Complete the main method body based on dfs
final int[] dx = new int[]{-1, 0, 1, 0}; // Row number change final int[] dy = new int[]{0, -1, 0, 1}; // Column number change for (int i = 0; i < rows; i++) for (int j = 0; j < cols; j++) { if (board[i][j] == 'X') continue; // Search in four directions for each point for (int dir = 0; dir < 4; dir++) { int next_x = i + dx[dir]; int next_y = j + dy[dir]; // If the next is directly out of bounds, the current i and j are adjacent to the boundary if (next_x < 0 || next_x >= rows || next_y < 0 || next_y >= cols) { int cur = find(getIndex(i, j)); if (cur != outer) fa[cur] = outer; // Connect to external } else if (board[next_x][next_y] == 'O') { // Conversely, if next is O, you need to merge int a = find(getIndex(next_x, next_y)); int b = find(getIndex(i, j)); // If the next is already in the outer set, put the current getIndex(i, j) into the outer set if (a == outer) fa[b] = outer; else // Otherwise, put next into the collection of getIndex(i, j) fa[a] = b; } } }
So the answer is to find 'O' that does not enter the outer set and turn it into 'X'
int[] fa; int rows, cols; public void solve(char[][] board) { // Upper left lower right final int[] dx = new int[]{-1, 0, 1, 0}; // Row number change final int[] dy = new int[]{0, -1, 0, 1}; // Column number change this.cols = board[0].length; this.rows = board.length; final int outer = rows * cols; fa = new int[rows * cols + 1]; // Joint search set // init for (int i = 0; i < rows; i++) for (int j = 0; j < cols; j++) fa[getIndex(i, j)] = getIndex(i, j); fa[outer] = outer; // dfs for (int i = 0; i < rows; i++) for (int j = 0; j < cols; j++) { if (board[i][j] == 'X') continue; // Search in four directions for each point for (int dir = 0; dir < 4; dir++) { int next_x = i + dx[dir]; int next_y = j + dy[dir]; // If the next is directly out of bounds, the current i and j are adjacent to the boundary if (next_x < 0 || next_x >= rows || next_y < 0 || next_y >= cols) {// out int cur = find(getIndex(i, j)); if (cur != outer) fa[cur] = outer; // Connect to external } else if (board[next_x][next_y] == 'O') { // Conversely, if next is O, you need to merge int a = find(getIndex(next_x, next_y)); int b = find(getIndex(i, j)); // If the next is already in the outer set, put the current getIndex(i, j) into the outer set if (a == outer) fa[b] = outer; else // Otherwise, put next into the collection of getIndex(i, j) fa[a] = b; } } } for (int i = 0; i < rows; i++) for (int j = 0; j < cols; j++) if (board[i][j] == 'O' && find(getIndex(i, j)) != outer) // Not connected to external board[i][j] = 'X'; } // 2D subscript to 1D private int getIndex(int i, int j) { return i * cols + j; } private int find(int x) { return x== fa[x] ? x: (fa[x] = find(fa[x])); }