Data structure: parallel query set: "can you seconds me for a statement? I am on the spot..."

Keywords: data structure

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]));
}

Posted by jami3 on Sat, 18 Sep 2021 03:42:21 -0700