Day 12 图的最小生成树MST(prim,kruskal)

最小生成树 给定一个带权的无向连通图 选取边将所有顶点连起来的同时 边的和的权值最小 即为最小生成树

prim算法

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

public class PrimTest {
    public static void main(String[] args) {
        //普利姆算法 求图的最小生成树  普利姆算法生成的边的数量为 节点数量-1
        pmap map = new pmap(7);
        char []node = {'A','B','C','D','E','F','G'};
        int [][]nodeMap = {
                {0,5,7,0,0,0,2},
                {5,0,0,9,0,0,3},
                {7,0,0,0,8,0,0},
                {0,9,0,0,0,4,0},
                {0,0,8,0,0,5,4},
                {0,0,0,4,5,0,6},
                {2,3,0,0,4,6,0},
        };
        map.fastSetMap(node,nodeMap);
        //显示图
        map.showMap();
        System.out.println("普利姆算法");
        int count = map.prim(map);
        System.out.println("总距离"+count);
    }
}
//图
class pmap{
    int nodeOrder;//节点的数量
    char []node;//节点
    int [][]nodeMap;//图的邻接矩阵

    public pmap(int nodeOrder){
        this.nodeOrder = nodeOrder;
        node = new char[nodeOrder];
        nodeMap = new int[nodeOrder][nodeOrder];
    }
    //因为只是为了测试生成 最小生成树  所以就快捷的创建一个图
    public void fastSetMap(char []node,int [][]nodeMap){
        this.node = node;
        this.nodeMap = nodeMap;
    }
    //显示图的邻接矩阵
    public void showMap(){
        for (int []i:nodeMap){
            System.out.println(Arrays.toString(i));
        }
    }
    //接收一个图 然后生成他的最小生成树
    public int prim(pmap map){
        //总距离
        int count = 0;
        //这是还没有加入到最小生成树内的 节点
        ArrayList<Integer> notDone = new ArrayList<Integer>();
        //当前还没有开始生成 所以把所有节点(的索引)都加入到 没有访问(加入最小生成树) 的集合中
        for (int i=0;i<map.node.length;i++){
            notDone.add(i);
        }
        //这是已经加入到最小生成树内的 节点  默认为空
        ArrayList<Integer> haveDone = new ArrayList<Integer>();
        //从未访问的集合中 取出一位 加入已访问的集合中 意思是从这一位开始生成 无论从哪一个生成 只会影响顺序 不会影响总距离
        haveDone.add(notDone.remove(0));
        //循环节点数-1次 因为边最多这么多条
        for (int j=1;j<map.node.length;j++){
            int min = 100;//应该根据邻接矩阵中的最大值来赋值 不会出错 为了方便暂时用100来测试
            int b1 = -1;
            int b2 = -1;
            int sign = -1;
            for (int k=0;k<haveDone.size();k++){
                for (int q=0;q<notDone.size();q++){
                    int value = map.nodeMap[haveDone.get(k)][notDone.get(q)];
                    if (value>0&&value<min){
                        min = value;
                        b1 = haveDone.get(k);
                        b2 = notDone.get(q);
                        sign = q;
                    }
                }
            }
            if (b1!=-1&&b2!=-1){
                haveDone.add(notDone.remove(sign));
                count += map.nodeMap[b1][b2];
                System.out.println("本轮连接点:"+map.node[b1]+"->:"+map.node[b2]+"距离"+map.nodeMap[b1][b2]);
            }
        }
        return count;
    }
}

kruskal算法

package KruskalTest;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;

public class KruskalTest {
    public static void main(String[] args) {
        pmap map = new pmap(7);
        char []node = {'A','B','C','D','E','F','G'};
        int [][]nodeMap = {
                {0, 12, 0, 0, 0, 16, 14},
                {12, 0, 10, 0, 0, 7, 0},
                {0, 10, 0, 3, 5, 6, 0},
                {0,  0, 3, 0, 4, 0, 0},
                {0,  0, 5, 4, 0, 2, 8},
                {16, 7, 6, 0, 2, 0, 9},
                {14, 0, 0, 0, 8, 9, 0},
        };
        map.fastSetMap(node,nodeMap);
        System.out.println("图的邻接矩阵");
        map.showMap();
        System.out.println("该图的最少生成树(克鲁斯卡尔算法)");
        map.kruskal();
    }
}
//图
class pmap{
    int chang;
    int nodeOrder;//节点的数量
    char []node;//节点
    int [][]nodeMap;//图的邻接矩阵

    public pmap(int nodeOrder){
        this.nodeOrder = nodeOrder;
        node = new char[nodeOrder];
        nodeMap = new int[nodeOrder][nodeOrder];
    }
    //因为只是为了测试生成 最小生成树  所以就快捷的创建一个图
    public void fastSetMap(char []node,int [][]nodeMap){
        this.node = node;
        this.nodeMap = nodeMap;
        for (int i=0;i<node.length;i++){
            for (int j = i+1; j < node.length; j++) {
                if (nodeMap[i][j]!=0){
                    chang++;
                }
            }
        }
//        System.out.println(chang);
    }
    //显示图的邻接矩阵
    public void showMap(){
        for (int []i:nodeMap){
            System.out.println(Arrays.toString(i));
        }
    }
    //kruskal算法生成最小生成树  边的数量仍是顶点数量-1
    //获取边的总数量 排序 依次加入 结果树中 每次加入判断当前加入的边的 两端顶点的 当前终点 是否相同 相同的话会形成回路 所以只有不同的时候才加入结果树
    //kruskal算法
    public void kruskal(){
        //用来储存节点的终点 长度为节点数
        int []ends = new int[this.node.length];
        //最后生成最小生成树的边
        int index = 0;
        bian []k = new bian[this.node.length-1];
//        ArrayList<Integer> huan = new ArrayList<Integer>();
        //获取排序后的所有边
        ArrayList<bian> allBian = getAllBian();
//        System.out.println(allBian.size());
        for (int i=0;i<chang;i++){
            //获取当前边的起点的索引
            int start = getNodeSign(allBian.get(i).start);
            int end = getNodeSign(allBian.get(i).end);
            //获取上方两点的当前终点 并判断是否想等 如果不相等 继续运行
//            System.out.println("点"+start+"的终点为"+getNodeEnd(ends,start)+"   "+"点"+end+"的终点为"+getNodeEnd(ends,end));
            if (getNodeEnd(ends,start) != getNodeEnd(ends,end)){//!huan.contains(start)||!huan.contains(end)
                System.out.println("点"+start+"."+allBian.get(i).start+"的终点为"+getNodeEnd(ends,start)+"   "+"点"+end+"."+allBian.get(i).end+"的终点为"+getNodeEnd(ends,end));
                //标记新的终点 !!!!一定注意 这里设置的是当前边的起点(边a连b中的a)的终点 的终点 为 当前边的终点(边a连b中的b)的终点
                ends[getNodeEnd(ends,start)] = getNodeEnd(ends,end);
//                huan.add(start);
//                huan.add(end);
//                System.out.println(1);
                //将当前符合要求的边加入 结果中
                k[index] = allBian.get(i);
                index++;
            }
        }
        //遍历生成的最小生成树 验证..
        for (bian v:k){
            System.out.print(v+" ");
        }
    }
    //传入节点名称 获取节点索引
    public int getNodeSign(char a){
        for (int i=0;i<this.node.length;i++){
            if (a == this.node[i]){
                return i;
            }
        }
        return -1;
    }
    //获取当前图的所有边 返回 这些边构成的集合
    public ArrayList<bian> getAllBian(){
        //遍历邻接矩阵 在遍历列的时候 是从当前行数+1列开始的 这样可以避免 1-2 2-1 这种情况 和2-2的情况
        ArrayList<bian> allBian= new ArrayList<bian>();
        for (int i=0;i<this.node.length;i++){
            for (int j=i+1;j<this.node.length;j++){
                if (this.nodeMap[i][j]!=0){
                    //将有链接的边加入 allbian这个集合中
                    allBian.add(new bian(this.node[i],this.node[j],this.nodeMap[i][j]));
                }
            }
        }
        //对边进行排序(根据边的权值)
        Collections.sort(allBian);
        //返回排序完成的边
        return allBian;
    }
    //查询当前顶点的终点
    public int getNodeEnd(int []end,int i){
        while (end[i]!=0){
            i = end[i];
        }
        return i;
    }
}
//定义一个边类 方便记录
class bian implements Comparable<bian>{
    char start;//当前边的起点
    char end;//当前边要连接的点(终点)
    int value;//当前边的权值
    public bian(char start,char end,int value){
        this.start = start;
        this.end = end;
        this.value = value;
    }


    @Override
    public int compareTo(bian o) {
        return this.value - o.value;
    }

    @Override
    public String toString() {
        return start+"-"+value+"->"+end;
    }
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值