Disjoint sets

Disjoint sets study notes (python, C language)

This month, I have encountered and checked the problem three times when brushing Leetcode. The first two times were too difficult to let go. If you encounter it today, try to solve it first. I saw this knowledge in the book "Challenge Programming Competition" on the table of the junior Click here to write down notes.



Preface

Union search is a 树型data structure used to deal with some 不相交集合(disjoint sets)的合并及查询problems. It is often represented by forest in use.


1. What is combined search?

Union check set is a data structure used to manage the grouping of elements.
The following operations can be performed efficiently

  • Query whether element a and element b belong to the same group.
  • Combine the group of element a and element b

Grouping and corresponding examples
Grouping and corresponding examples

Second, the structure of the combined search set

And check the set is 树形结构. However, it is not a binary tree.

Each element corresponds to a node, and each group corresponds to a tree.

In the combined search center, you don’t need to pay attention to information such as which node is the parent of which node and the shape of the tree. The overall tree structure is the most important

1. Initialization

initialization

2. Merger

As in the figure below, from the root of one group to the edge of another group, two trees become one tree, that is, two groups become one group
Insert picture description here

3. Inquiry

In order to query whether two nodes are in the same group, just walk up the tree and query the 根节点是否相同same group when the root nodes are the same, otherwise different groups. As shown in the figure above, the root of (2) and (5) is (1), and the root of (7) is (6), so (2) and (5) are the same group, but (2) and (7) are not the same group.

Points to note in the implementation

In the tree data structure, if degradation occurs (the binary tree degenerates into a one-dimensional linked list), then the time complexity will become very high. In the combined search, the degradation can be avoided only by following the methods below.

  • For each tree, record the height of the tree (rank)
  • When merging, if the ranks of the two trees are different, the smaller rank is connected to the larger rank.

Insert picture description here
In addition, through path compression, concatenation can be made more efficient. For each node, once you go up to the root node once, change the edge from this point to the father to be directly connected to the root.
If you need to query (7), you can directly connect (7) to the root.
Insert picture description here
Above the second, not only the queried nodes, but all the nodes passed through in the query process can be directly connected to the root. When you query again, you can quickly find out who the root is.
As follows, connect (2) (3) (4) (5) to (1).
Insert picture description here
When using this simplification method, for simplicity, even if the height of the tree changes, the rank is no longer modified.

Check the complexity of union

After adding two optimizations, the efficiency of checking union is very high. The complexity of an operation on the union set of n elements is O (a (n)) O(a(n))O ( a ( n ) ) . Herea (n) a(n)a ( n ) is the inverse function of Ackermann function. This is better thanO (log (n)) O(log(n))O ( l o g ( n ) ) is even faster.

However, this is "amortized complexity". Not every time is satisfied, after many times, the average complexity is each time.

Realization of Concurrent Check Set

The array parrepresents the parent number, par [x] = x par[x] = xp a r [ x ]=when x ,xxx is the root of the tree

int par[MAX_N];    // 父亲
int rank[MAX_N];    // 树的高度

// 初始化n个元素
void init(int n) {
    
    
    for (int i = 0); i < n; i++){
    
    
        par[i] = i;
        rank[i] = 0;
        }
    }

// 查询树的根
int find(int x){
    
    
    if (par[x] == x):
        return x;
    }
    else{
    
    
    return par[x] = find(par[x]);
    }
}

// 查询x和y所属集合
void unite(int x, int y){
    
    
    x = find(x);
    y = find(y);
    if (x == y) return;
    
    if (rank[x] < rank[y]){
    
    
        par[x] = y;
        } else {
    
    
        par[y] = x;
        if (rank[x] == rank[y]) rank[x]++;
    }
}

// 判断x和y是否属于同一集合
bool same(int x, int y){
    
    
    return find(x) == find(y);
}

python implementation

class UnionFind:
    def __init__(self, nums):
        self.par = list(range(len(nums)))
        self.rank = [0] * len(nums)
    
    # 查询树的根   
    def find(self, x):
        if self.par[x] == x:
            return x
        else:
            return self.find(self.par[x])
        
    # 查询x个y所属集合   
    def unite(self, x, y):
        x = self.find(x)
        y = self.find(y)
        if x == y:
            return
        if self.rank[x] < self.rank[y]:
            self.par[x] = y
        else:
            self.par[y] = x
            if self.rank[x] == self.rank[y]:
                self.rank[x] += 1
    
    # 判断x和y是否为同一集合
    def same(self, x, y):
        return self.find(x) == self.find(y)

Example LeetCode 1202. Swap elements in a string

Topic link:: Exchange elements in a string .
Topic:
Give you a string s and some "index pairs" array pairs in the string, where pairs[i] = [a, b] means the string Two indexes (numbering starts from 0).

You can swap the characters at any pair of indexes in pairs as many times as you want .

Return the smallest string lexicographically that s can become after several exchanges.

示例 1:

输入:s = "dcab", pairs = [[0,3],[1,2]]
输出:"bacd"

解释: 
交换 s[0] 和 s[3], s = "bcad"
交换 s[1] 和 s[2], s = "bacd"

示例 2:

输入:s = "dcab", pairs = [[0,3],[1,2],[0,2]]
输出:"abcd"

解释:
交换 s[0] 和 s[3], s = "bcad"
交换 s[0] 和 s[2], s = "acbd"
交换 s[1] 和 s[2], s = "abcd"

Answer code:

class UnionFind:
    def __init__(self, nums):
        self.par = list(range(len(nums)))
        self.rank = [0] * len(nums)
    
    # 查询树的根   
    def find(self, x):
        if self.par[x] == x:
            return x
        else:
            return self.find(self.par[x])
        
    # 查询x个y所属集合   
    def unite(self, x, y):
        x = self.find(x)
        y = self.find(y)
        if x == y:
            return
        if self.rank[x] < self.rank[y]:
            self.par[x] = y
        else:
            self.par[y] = x
            if self.rank[x] == self.rank[y]:
                self.rank[x] += 1
    
    # 判断x和y是否为同一集合
    def same(self, x, y):
        return self.find(x) == self.find(y)

class Solution:
    def smallestStringWithSwaps(self, s: str, pairs: List[List[int]]) -> str:
        uf = UnionFind(s)
        i = 0
        # 寻找根节点
        for k, v in pairs:
            uf.unite(k, v)
        
        # 获取根节点对应的连通块集合
        dic = collections.defaultdict(list)
        for i in range(len(s)):
            root = uf.find(i)
            dic[root].append(i)
        # 对每个连通块中元素排序
        res = list(s)
        for k, v in dic.items():
            arr = [s[i] for i in v]
            arr.sort()
            for i in range(len(v)):
                res[v[i]] = arr[i]
                
        return "".join(res)

to sum up

He vomited blood and sorted it out, but I was too disheveled. I watched it all day. Come on!

Guess you like

Origin blog.csdn.net/qq_42388742/article/details/112464016