Estructura de datos y algoritmo (35) del algoritmo de Kruskal

Demostración diaria de estructuras de datos de aprendizaje y algoritmos.

Introducción al algoritmo de Kruskal

Inserte la descripción de la imagen aquí

Ejemplo de algoritmo de Kruskal

Inserte la descripción de la imagen aquí

Ilustración del problema

Seleccione n-1 bordes en un gráfico conectado con n vértices para formar un subgrafo conectado mínimo, y haga que la suma de los pesos en n-1 bordes en el subgrafo conectado alcance el mínimo, luego llámelo conectado El árbol de expansión mínimo de la red.
Inserte la descripción de la imagen aquí
Inserte la descripción de la imagen aquí

  • Paso 1:
    Agregue el borde <E, F> a R. El borde <E, F> tiene el menor peso, por lo que se agrega al resultado mínimo del árbol de expansión R.
  • Paso 2:
    Agregue el borde <C, D> a R. Después de la operación anterior, el peso del borde <C, D> es el más pequeño, por lo que se agrega al resultado mínimo del árbol de expansión R.
  • Paso 3:
    Agregue el borde <D, E> a R.
    Después de la operación anterior, el peso del borde <D, E> es el más pequeño, por lo que se agrega al resultado mínimo del árbol de expansión R.
  • Paso 4:
    Agregue el borde <B, F> a R. Después de la operación anterior, el peso del borde <C, E> es el más pequeño, pero <C, E> formará un bucle con el borde existente; por lo tanto, omita el borde <C, E>. Del mismo modo, omita el borde <C, F>. Agregue aristas <B, F> al resultado mínimo del árbol de expansión R.
  • Paso 5:
    Agregue el borde <E, G> a R. Después de la última operación, el peso del borde <E, G> es el más pequeño, por lo que se agrega al resultado mínimo del árbol de expansión R.
  • Paso 6:
    Agregue el borde <A, B> a R. Después de la operación anterior, el peso del borde <F, G> es el más pequeño, pero <F, G> formará un bucle con el borde existente; por lo tanto, omita el borde <F, G>. Del mismo modo, omita el borde <B, C>. Agregue el borde <A, B> al resultado mínimo del árbol de expansión R.
    En este punto, la construcción mínima del árbol de expansión está completa. Los bordes que incluye son: <E, F> <C, D> <D, E> <B, F> <E, G> <A, B>.

Análisis de algoritmos

De acuerdo con la idea básica y el método del algoritmo Cruze presentado anteriormente, podemos entender que el algoritmo Cruze se enfoca en los siguientes dos problemas que deben resolverse: el
problema es que todos los bordes de un par de gráficos están ordenados de acuerdo con el valor del peso.
Pregunta 2: Al agregar aristas al árbol de expansión mínimo, cómo juzgar si se forma un bucle.

  • El primer problema es usar un algoritmo de ordenación para ordenar.
  • Problema dos, el método de procesamiento es: registrar el punto final del vértice en el "árbol de expansión más pequeño", y el punto final del vértice es "el vértice más grande conectado a él en el árbol de expansión mínimo". Luego, cada vez que se necesita agregar un borde al árbol de expansión mínimo, se juzga si los puntos finales de los dos vértices del borde coinciden, y si coinciden, se formará un bucle.
Determinar si generar un bucle

Inserte la descripción de la imagen aquí
Notas sobre el punto final:

  1. Es organizar todos los vértices en orden de pequeño a grande; el punto final de un cierto vértice es "el vértice más grande conectado a él".
  2. Por lo tanto, a continuación, aunque <C, E> es el borde con el menor peso. Sin embargo, los puntos finales de C y E son ambos F, es decir, sus puntos finales son los mismos, por lo tanto, si se agrega <C, E> al árbol de expansión mínima, se formará un bucle. Así es como se juzga el circuito. Es decir, los dos vértices del borde que unimos no pueden apuntar al mismo punto final, de lo contrario formará un bucle.

Implementación de código

public class KruskalDemo {

    private int edgeNum;    // 边的个数
    private char[] vertexs; // 顶点数目
    private int[][] matrix; // 邻接矩阵
    private static final int INF = Integer.MAX_VALUE;   // 使用INF表示两个顶点不能连通

    public KruskalDemo(char[] vertexs, int[][] matrix) {
        this.vertexs = vertexs;
        this.matrix = matrix;
        // 统计边的条数
        for (int i = 0; i < vertexs.length; i++) {
            for (int j = 0; j < vertexs.length; j++) {
                if (this.matrix[i][j] != INF) {
                    this.edgeNum++;
                }
            }
        }
    }

    public void print() {
        System.out.println("邻接矩阵为:");
        for (int[] ints : this.matrix) {
            for (int anInt : ints) {
                System.out.printf("%12d", anInt);
            }
            System.out.println();
        }
    }


    // 返回顶点的下标
    private int getPosition(char c) {
        return Arrays.binarySearch(this.vertexs, c);
    }

    // 获取图中所有的边,后面需要遍历该集合
    // 通过邻接矩阵来获取
    private ArrayList<EData> getEdges() {
        int index = 0;
        ArrayList<EData> datas = new ArrayList<>();
        for (int i = 0; i < vertexs.length; i++) {
            for (int j = i + 1; j < vertexs.length; j++) {
                if (matrix[i][j] != INF) {
                    datas.add(new EData(vertexs[i], vertexs[j], matrix[i][j]));
                }
            }
        }
        return datas;
    }

    // 获取下标为i的顶点的终点
    // ends[]记录了各个顶点对应的终点是哪一个,ends数组是在遍历过程中逐步形成的
    private int getEnd(int[] ends, int i) {
        while (ends[i] != 0) {
            i = ends[i];
        }
        return i;
    }

    public void kruskal() {
        int index = 0;                          // 表示最后结果数组的索引
        int[] ends = new int[this.edgeNum];     // 保存已有的最小生成树,每个顶点在最小生成树中的终点
        // 创建结果集,保存最小生成树
        ArrayList<EData> rets = new ArrayList<>();
        // 获取所有边集合
        ArrayList<EData> edges = getEdges();
        // 对边集合进行排序
        Collections.sort(edges);
        // 遍历edges,将边添加到最小生成树中时,判断准备的边是否形成了回路,没有才加入
        for (int i = 0; i < edges.size(); i++) {
            // 获取第i条边的第一个顶点的下标
            int p1 = getPosition(edges.get(i).start);   // <E,F> E:4    <E,G>
            // 获取第i条边的第二个顶点的下标
            int p2 = getPosition(edges.get(i).end);     // F:5           G:6
            // 获取p1下标顶点在已有的最小生成树中对应的终点
            int m = getEnd(ends, p1);                   // Em:4          Em:5
            // 获取p2下标顶点在已有的最小生成树中对应的终点
            int n = getEnd(ends, p2);                   // Fn:5          Gn:6
            // 判断是否构成回路即m,n是否相等
            if (m != n) {
                // 不构成回路
                // 设置m在已有最小生成树中的终点
                // 不需要ends[n] = n;
                ends[m] = n;                // ends[E:4] = F:5
                rets.add(edges.get(i));     // 加入最小生成树集合
            }
        }
        // 输出最小生成树
        System.out.println("最小生成树:");
        System.out.println(rets);
    }

    public static void main(String args[]) {
        char[] vertexs = {'A', 'B', 'C', 'D', 'E', 'F', 'G'};
        //克鲁斯卡尔算法的邻接矩阵
        int matrix[][] = {
                    /*A*//*B*//*C*//*D*//*E*//*F*//*G*/
                /*A*/ {0, 12, INF, INF, INF, 16, 14},
                /*B*/ {12, 0, 10, INF, INF, 7, INF},
                /*C*/ {INF, 10, 0, 3, 5, 6, INF},
                /*D*/ {INF, INF, 3, 0, 4, INF, INF},
                /*E*/ {INF, INF, 5, 4, 0, 2, 8},
                /*F*/ {16, 7, 6, INF, 2, 0, 9},
                /*G*/ {14, INF, INF, INF, 8, 9, 0}};

        KruskalDemo kruskalDemo = new KruskalDemo(vertexs, matrix);
        kruskalDemo.print();
        kruskalDemo.kruskal();
    }
}

// 边对象
class EData implements Comparable<EData> {
    char start;     // 边的起点
    char end;       // 边的终点
    int weight;     // 边的权值

    public EData(char start, char end, int weight) {
        this.start = start;
        this.end = end;
        this.weight = weight;
    }

    @Override
    public String toString() {
        return "<" + start +
                "," + end +
                "> 权值=" + weight;
    }

    @Override
    public int compareTo(EData o) {
        return this.weight - o.weight;
    }
}

Clave de algoritmo

<C, E> como ejemplo
// 获取下标为i的顶点的终点
    // ends[]记录了各个顶点对应的终点是哪一个,ends数组是在遍历过程中逐步形成的
    private int getEnd(int[] ends, int i) {
        while (ends[i] != 0) {
            i = ends[i];
        }
        return i;
    }

Inserte la descripción de la imagen aquí
Inserte la descripción de la imagen aquí
Inserte la descripción de la imagen aquí
Inserte la descripción de la imagen aquí
Los puntos finales de C y E son los mismos (F), formando un bucle, por lo que no se cumplen las condiciones.
Inserte la descripción de la imagen aquí

GitHub: estructura de datos y código fuente del algoritmo

83 artículos originales publicados · Me gusta 23 · Visita 3529

Supongo que te gusta

Origin blog.csdn.net/qq_44779506/article/details/105367179
Recomendado
Clasificación