[Estructura de datos de alto nivel] Explicación detallada de la colección

Resumen de consolidación

Disjoint-set (Union Find), también conocido como disjoint sets (Disjiont Set), que se aplica a un conjunto de N elementos de demanda y el problema de consulta en este escenario, generalmente hacemos al comienzo de cada elemento constituye un Single- colección de elementos , y luego fusionar las colecciones donde los elementos que pertenecen al mismo grupo están ubicados en un orden determinado , durante el cual es necesario encontrar repetidamente en qué colección se encuentra un elemento. Aunque el problema no es complicado, las estructuras de datos ordinarias a menudo no pueden resolver el problema cuando se enfrentan a una gran cantidad de datos, y la búsqueda y recopilación es el algoritmo más excelente para resolver este tipo de problema.

Ejemplo de proceso de algoritmo:
Inserte la descripción de la imagen aquí

Análisis de la función de verificación y configuración concurrentes

Inicialmente:

元素:0 1 2 3 4 5 6 7 
集合:0 1 2 3 4 5 6 7 

Inserte la descripción de la imagen aquí
Unión: Unión (0,5)

元素:0 1 2 3 4 5 6 7 
集合:0 1 2 3 4 0 6 7 

Aquí, fusiona 5 en el conjunto 0. (Por supuesto, también puede poner 0 en el conjunto de Wuzai 5.)
Es decir, 5 reconocerá a 0 como el jefe y 0 será el jefe del conjunto.
Inserte la descripción de la imagen aquí

Find(0== 0
Find(5== 0
Find(0== Find(5// 在同一个集合
Find(2== 2
Find(5== 0
Find(2) != Find(5// 不在同一个集合

Fusión: Unión (2,4)

元素:0 1 2 3 4 5 6 7 
集合:0 1 2 3 2 0 6 7 

Combine 4 en el conjunto 2. De manera similar, 2 es el jefe del conjunto.
Inserte la descripción de la imagen aquí
Fusionar: Unión (0,4) Los
primeros 4 ya pertenecen al conjunto 2, fusionar 2 conjunto a 0 conjunto:
originalmente el jefe de 4 era 2, ahora se fusionó con 0, por lo que aquí también puede reconocer al jefe de 2 como 0 .
equivalente Ambos 4 y 2 adoran al nuevo jefe 0.

元素:0 1 2 3 4 5 6 7 
集合:0 1 0 3 0 0 6 7 

Inserte la descripción de la imagen aquí


Find(2== Find(5// 在同一个集合

Implementación de matriz y conjunto de consultas

/**
 * @ClassName Union_Find
 * @Description :TODO
 * @Author Josvin
 * @Date 2021/01/14/21:00
 */

class UnionFind {
    
    
    private int[] id;// 存储各个元素属于那个集合,他们的下标值表示他们的元素,值表示他们属于哪个集合
    private int count;// 表示刚开始有多少集合(初始每个元素就是一个集合)

    public UnionFind(int N) {
    
    
        // 初始化
        count = N;
        id = new int[N];
        for(int i = 0; i < N; i++) id[i] = i;
    }

    // 获取有多少集合
    public int getCount() {
    
    
        return count;
    }

    //判断 p 和 q 是不是一个集合
    public boolean connected(int p, int q) {
    
    
        return find(p) == find(q);
    }

    // 查找(找最后被合并到那个集合,也就是找集合老大)
    public int find(int p) {
    
    
        return id[p];
    }

    // 合并 (吧一个集合的合并到另一个集合,也就是换一个集合的老大)
    public void union(int p, int q){
    
    
        int pRoot = find(p);
        int qRoot = find(q);

        if(pRoot == qRoot) return;

        for(int i = 0; i < id.length; i++)
            if(id[i] == pRoot)  id[i] = qRoot;
        count--;// 当没合并一次,集合的数目就会少一个
    }
}

La complejidad aquí todavía es relativamente alta, principalmente la complejidad del proceso de fusión es O (n)
A continuación, presentaremos otros métodos de implementación de búsqueda de fusión .

Consolidación-Realización forestal

Usando el bosque para almacenar la relación entre las colecciones , diferentes elementos pertenecientes a la misma colección tienen el mismo nodo raíz , que representa esta colección.
Cuando se busca a qué conjunto pertenece un elemento, atraviesa el elemento hasta el nodo raíz y devuelve el conjunto representado por el nodo raíz ; en el proceso transversal, el algoritmo de optimización de compresión de ruta se utiliza para hacer que la forma del árbol general sea más plana , optimizando así el tiempo de consulta . Complejidad .
Al fusionarse , los dos subárboles se fusionan en un árbol, y el nodo raíz de un subárbol apunta al nodo raíz del otro subárbol; al fusionarse, el subárbol más pequeño se puede ajustar de acuerdo con el tamaño del subárbol. El árbol es fusionados en un subárbol más grande, de modo que el tamaño del árbol sea más equilibrado , optimizando así la complejidad temporal de las consultas futuras.

Diagrama de ejemplo:
Inserte la descripción de la imagen aquí

Algoritmo de búsqueda de conjuntos de búsqueda de unión

Al realizar una búsqueda, la búsqueda ordinaria es atravesar el nodo raíz a través de la matriz id. Cuando p es diferente del conjunto actual id [p] (hasta que py id [p] sean iguales, salta del bucle), realiza el ciclo: p = id [p];
Devuelve el valor de p.

public int find(int p) {
    
    
        while(p != id[p]) p = id[p];
        return p;
    }

Aumente el algoritmo de optimización de la compresión de ruta al buscar:
cuando p es diferente del id del conjunto actual [p] (hasta que p y el id [p] sean iguales, salte del bucle), bucle:
actualice el id del nodo principal [p ] de p al id del nodo principal de id [p] [id [p]];
p = id [p];
devuelve el valor de p.

public int find(int p) {
    
    
        if(p != id[p]) id[p] = find(id[p]);
        return id[p];
    }

Lo anterior es una escritura recursiva;
escritura en bucle:

public int find(int p) {
    
    
        while(p != id[p]) {
    
    
        	id[p] = id[id[p]];
        	p = id[p];
        }
        return p;
    }

Ilustración:
Inserte la descripción de la imagen aquí

Algoritmo de combinación de conjuntos de búsqueda de unión

Cuando el conjunto se fusiona, los dos subárboles se fusionan en un árbol, y el nodo raíz de un árbol apunta al nodo raíz del otro subárbol. Cuando se fusiona, el subárbol más pequeño se puede cambiar según el tamaño del subárbol. El árbol se fusiona en un subárbol de mayor escala para hacer que el árbol esté más equilibrado, optimizando así la complejidad temporal de las consultas futuras.
Combine el conjunto donde se ubica p con el conjunto donde se ubica q:
encuentre la raíz del conjunto donde se ubica p, i = encuentre (p)
encuentre la raíz del conjunto donde se ubique q, j = encuentre (q)
si iyj son iguales, regresa directamente;

Si el tamaño del subárbol donde se encuentra i es menor que el tamaño del subárbol donde se encuentra j:
apunte la raíz de i a j;
aumente el tamaño de j al tamaño del subárbol i; de lo
contrario:
apunte la raíz de j a i;
aumente el tamaño de i al tamaño del subárbol j;

Número de subárboles menos 1

Inserte la descripción de la imagen aquí
Como puede ver desde la parte superior, el árbol está más equilibrado al fusionar subárboles más pequeños en subárboles más grandes.



public void union(int p, int q){
    
    
        int pRoot = find(p);
        int qRoot = find(q);

        if(pRoot == qRoot) return;

        if(sz[pRoot] < sz[qRoot]) {
    
     id[pRoot] = qRoot; sz[qRoot] += sz[pRoot]; }
        else                      {
    
     id[qRoot] = pRoot; sz[pRoot] += sz[qRoot]; }
        count--;
    }

Código general

public class UnionFind {
    
    
    private int[] id;
    private int count;
    private int[] sz;

    public UnionFind(int N) {
    
    
        count = N;
        id = new int[N];
        sz = new int[N];
        for(int i = 0; i < N; i++) {
    
    
            id[i] = i;
            sz[i] = 1;
        }
    }

    public int getCount() {
    
    
        return count;
    }

    public boolean connected(int p, int q) {
    
    
        return find(p) == find(q);
    }

    public int find(int p) {
    
    
        if (p != id[p]) id[p] = find(id[p]);
        return id[p];
    }

    public void union(int p, int q){
    
    
        int pRoot = find(p);
        int qRoot = find(q);

        if(pRoot == qRoot) return;

        if(sz[pRoot] < sz[qRoot]) {
    
     id[pRoot] = qRoot; sz[qRoot] += sz[pRoot]; }
        else                      {
    
     id[qRoot] = pRoot; sz[pRoot] += sz[qRoot]; }
        count--;
    }
}

Supongo que te gusta

Origin blog.csdn.net/weixin_45532227/article/details/112634971
Recomendado
Clasificación