天天看點

貪心算法_最小生成樹_Kruskal(克魯斯卡爾)算法

/**
 * 8.貪心算法_最小生成樹_Kruskal(克魯斯卡爾)算法
 * @author Matt
 */
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashSet;

public class Kruskal {
    /**
     * 建立一個Edge邊類
     * @author Matt
     */
    public static class Edge implements Comparable<Edge> {
        // 分别代表起始節點、終止節點、權值
        int u, v, w;
        // 構造器
        public Edge(int u, int v, int w) {
            this.u = u;
            this.v = v;
            this.w = w;
        }

        @Override
        // 排序,按權值從小到大排序
        public int compareTo(Edge o) {
            int ow = o.w;
            if (w < ow)
                return -;
            if (w == ow)
                return ;
            return ;
        }

    }

    /**
     * 克魯斯卡爾算法
     * @param V 序号
     * @param E 邊節點
     */
    public static void Kruskal(int[] V, Edge[] E) {
        Arrays.sort(E);// 将邊按照權重w升序排序
        // 建立一個ArrayList容器來儲存所有邊的表集合
        ArrayList<HashSet> sets = new ArrayList<HashSet>();
        // 從0到6進行周遊
        for (int i = ; i < V.length; i++) {
            // 建立一個哈希表
            HashSet set = new HashSet();
            // 從1開始設定邊的序号,一共5個邊
            set.add(V[i]);
            // 将表添加到容器中
            sets.add(set);
        }
        // 周遊每一條邊
        for (int i = ; i < E.length; i++) {
            // 取出每一個邊節點的起始位置和終止位置
            int start = E[i].u, end = E[i].v;
            // 有些節點之間不同,賦為負值
            int counti = -, countj = -;
            // 從0到6周遊容器
            for (int j = ; j < sets.size(); j++) {
                // 取出容器中每一個邊的表
                HashSet set = sets.get(j);
                // 如果存在資料,counti指派為起始節點
                if (set.contains(start)) {
                    counti = j;
                }
                // 如果存在資料,countj指派為終止節點
                if (set.contains(end)) {
                    countj = j;
                }
            }
            // 若為負值則錯誤
            if (counti <  || countj < )
                System.err.println("沒有在子樹中找到節點,錯誤");
            // 起始節點和終止節點不為同一個
            if (counti != countj) {
                // 列印路徑
                System.out.println(
                        "start = " + start + 
                        ", end = " + end + 
                        ", weight = " + E[i].w);
                // 從容器中取出序号為終止節點的邊
                HashSet setj = sets.get(countj);
                sets.remove(countj); // 移除該邊
                // 從容器中取出序号為起始節點的邊
                HashSet seti = sets.get(counti);
                sets.remove(counti); // 移除該邊
                // 設定起始節點為終止節點(進行兩點連接配接)
                seti.addAll(setj);
                // 将該邊添加至容器
                sets.add(seti);
            } 
        }

    }

    public static void main(String[] args) {
        // 初始化
        int[][] tree = { 
                { -, , , , -, - }, 
                { , -, , -, , - }, 
                { , , -, , ,  },
                { , -, , -, -,  }, 
                { -, , , -, -,  }, 
                { -, -, , , , - } };
        // 建立V和E數組
        int[] V = { , , , , ,  };
        Edge[] E = new Edge[];
        E[] = new Edge(, , );
        E[] = new Edge(, , );
        E[] = new Edge(, , );
        E[] = new Edge(, , );
        E[] = new Edge(, , );
        E[] = new Edge(, , );
        E[] = new Edge(, , );
        E[] = new Edge(, , );
        E[] = new Edge(, , );
        E[] = new Edge(, , );
        Kruskal(V, E);
    }
}
// 運作結果:
//  start = 1, end = 3, weight = 1
//  start = 4, end = 6, weight = 2
//  start = 2, end = 5, weight = 3
//  start = 3, end = 6, weight = 4
//  start = 2, end = 3, weight = 5