ICode9

精准搜索请尝试: 精确搜索
首页 > 编程语言> 文章详细

数据结构与算法---23.prim普利姆算法、kruskal克鲁斯卡尔算法

2021-08-07 14:06:19  阅读:153  来源: 互联网

标签:10000 weight 23 int graph 算法 prim data public


1.prim普利姆算法

 

普里姆算法介绍

普利姆(Prim)算法求最小生成树,也就是在包含n个顶点的连通图中,找出只有(n-1)条边包含所有n个顶点的连通子图,也就是所谓的极小连通子图。

代码实现: 

import java.util.Arrays;

public class PrimAlgorithm {
    public static void main(String[] args) {

        char[] data = {'A','B','C','D','E','F','G'};
        int numNode= data.length;
        int[][] weight={
                {10000,5,7,10000,10000,10000,2},
                {5,10000,10000,9,10000,10000,3},
                {7,10000,10000,10000,8,10000,10000},
                {10000,9,10000,10000,10000,4,10000},
                {10000,10000,8,10000,10000,5,4},
                {10000,10000,10000,4,5,10000,6},
                {2,3,10000,10000,4,6,10000}
        };

        //创建一个MGraph对象
        MGraph graph=new MGraph(numNode);
        //创建一个MinTree对象
        MinTree minTree=new MinTree();
        //创建图
        minTree.createGraph(graph,numNode,data,weight);

        //显示图的邻接矩阵
        minTree.showGraph(graph);

        //测试prim算法
        minTree.prim(graph,0);
    }
}

//创建最小生成树
class MinTree{

    /**
     * 创建图
     * @param graph 图对象
     * @param numNode 图的顶点个数
     * @param data 图的各顶点值
     * @param weight 图邻接矩阵
     */
    public void createGraph(MGraph graph,int numNode,char[] data,int[][] weight){

        for (int i = 0; i < numNode; i++) {
            graph.data[i]=data[i];
            for (int j = 0; j < numNode; j++) {
                graph.weight[i][j]=weight[i][j];
            }
        }
    }

    //显示图的邻接矩阵
    public void showGraph(MGraph graph){

        for (int[] link:graph.weight) {
            System.out.println(Arrays.toString(link));
        }
    }

    /**
     * 编写一个prim算法,得到最小生成树
     * @param graph 传入的图
     * @param v 表示从图的第几个顶点开始生成 'A' -> 0 'B' -> 1
     */
    public void prim(MGraph graph,int v){

        //visited[]数组标记顶点是否被访问过,初始化为0,默认元素都没有被访问过
        int[] visited=new int[graph.numNode];
        visited[v]=1;

        int maxWeight=10000;
        //用h1和h2记录两个定点的下标
        int h1=-1;
        int h2=-1;

        for (int k = 1; k < graph.numNode; k++) {//有graph.numNode个顶点的话,生成graph.numNode - 1条边

            for (int i = 0; i < graph.numNode; i++) {//i节点:找到访问过的节点
                for (int j = 0; j < graph.numNode; j++) {//j节点:找到没有访问过的节点

                    if (visited[i]==1&&visited[j]==0&&graph.weight[i][j]<maxWeight){

                        //替换maxWeight
                        maxWeight=graph.weight[i][j];
                        //记录最后最小权值对应的两个顶点
                        h1=i;
                        h2=j;
                    }
                }
            }
            //此时h1和h2记录了一条最小的边
            System.out.println("边<" + graph.data[h1] + "," + graph.data[h2] + "> 权值:" + maxWeight);
            //将当前这个节点标记为已经访问过的节点
            visited[h2]=1;
            //maxWeight重置为10000
            maxWeight=10000;
        }
    }
}

//创建图 每一个MGraph对象表示一个图
class MGraph{

    int numNode;//表示图的节点个数
    char[] data;//存放节点数据
    int[][] weight;//存放边,也就是邻接矩阵

    public MGraph(int numNode) {
        this.numNode = numNode;
        data=new char[numNode];
        weight=new int[numNode][numNode];
    }
}

3.kruskal克鲁斯卡尔算法

1) 某城市新增7个站点(A, B, C, D, E, F, G) ,现在需要修路把7个站点连通

2) 各个站点的距离用边线表示(权) ,比如 A – B 距离 12公里

3) 问:如何修路保证各个站点都能连通,并且总的修建公路总里程最短?

也是求最小生成树

1) 克鲁斯卡尔(Kruskal)算法,是用来求加权连通图的最小生成树的算法。

2) 基本思想:按照权值从小到大的顺序选择n-1条边,并保证这n-1条边不构成回路

3) 具体做法:首先构造一个只含n个顶点的森林,然后依权值从小到大从连通网中选择边加入到森林中,并使森林中不产生回路,直至森林变成一棵树为止

 

 

 

 

 

代码实现

import java.util.Arrays;
import java.util.Comparator;

import static com.gou.kruskal.KruskalCase.INF;

public class KruskalCase {

    public static final  int INF=Integer.MAX_VALUE;

    public static char[] data = {'A','B','C','D','E','F','G'};
    public static int[][] weight ={
            {0,12,INF,INF,INF,16,14},
            {12,0,10,INF,INF,7,INF},
            {INF,10,0,3,5,6,INF},
            {INF,INF,3,0,4,INF,INF},
            {INF,INF,5,4,0,2,8},
            {16,7,6,INF,2,0,9},
            {14,INF,INF,INF,8,9,0}
    };

    public static int edgeNum;

    public static void main(String[] args) {

        //创建一个图对象
        Graph graph=new Graph(data.length);

        graph.data=data;
        graph.weight=weight;

        edgeNum = graph.getEdgeNum();

        kruskal();
    }

    //将边的数组按照权值大小进行排序
    public static void sortEData(EData[] edges){

        Arrays.sort(edges, new Comparator<EData>() {
            @Override
            public int compare(EData o1, EData o2) {
                return o1.weight-o2.weight;
            }
        });
    }

    //返回对应顶点的下标
    public static int getPosition(char ch){

        for (int i = 0; i < data.length; i++) {
            if (data[i]==ch){
                return i;
            }
        }
        //找不到
        return -1;
    }

    /**
     * 根据邻接矩阵获取图中的边的集合
     * EData[] 形式 [{'A','B',12},['B','F',7]...]
     * @return
     */
    public static EData[] getEdges(){

        EData[] edges=new EData[edgeNum];
        int index=0;

        for (int i = 0; i < data.length; i++) {
            for (int j = i+1; j < data.length; j++) {

                if (weight[i][j]!=INF){

                    edges[index]=new EData(data[i],data[j],weight[i][j]);
                    index++;
                }
            }
        }
        return edges;
    }

    /**
     * 功能:获取下标为i的顶点的终点(),用于后面判断两个顶点的终点是否相同
     * @param end 数组就是记录了各个顶点对应的终点是哪个,ends数组是在遍历过程中,逐步形成
     * @param i 表示传入的顶点对应的下标
     * @return 返回的就是下标为i的这个顶点对应的终点的下标
     */
    public static int getEnd(int[] end,int i){

        while (end[i]!=0){
            i=end[i];
        }
        return i;
    }

    public static void kruskal(){

        int index=0;//表示最后结果数组的索引

        int[] ends=new int[edgeNum];//用于保存"已有最小生成树"中的每个顶点在最小生成树中的终点

        //创建结果数组,保存最后的最小生成树
        EData[] res=new EData[edgeNum];

        //获取图中所有的边的集合
        EData[] edges = getEdges();

        //按照边的权值大小进行排序(从小到大)
        sortEData(edges);

        //遍历edges数组,将边添加到最小生成树中时,判断是准备加入的边否形成了回路,如果没有,就加入rets,否则不能加入
        for (int i = 0; i < edgeNum; i++) {
            //获取到第i条边的第一个顶点(起点)
            int p1 = getPosition(edges[i].start);
            //获取到第i条边的第2个顶点
            int p2 = getPosition(edges[i].end);

            //获取p1这个顶点在已有最小生成树中的终点
            int m = getEnd(ends, p1);
            //获取p2这个顶点在已有最小生成树中的终点
            int n = getEnd(ends, p2);

            //是否构成回路
            if (m!=n){
                ends[m]=n;
                res[index++]=edges[i];
            }
        }
        System.out.println("最小生成树为");
        for (int i = 0; i < index; i++) {
            System.out.println(res[i]);
        }
    }
}

class Graph{

    int edgeNum;//边的数量
    int nodeNum;//顶点数量
    char[] data;//顶点的数值
    int[][] weight;//邻接矩阵

    public Graph(int nodeNum) {
        this.nodeNum = nodeNum;
        data=new char[nodeNum];
        weight=new int[nodeNum][nodeNum];
        edgeNum=0;
    }

    //统计边的数量
    public int getEdgeNum(){

        for (int i = 0; i < data.length; i++) {
            for (int j = i+1; j < data.length; j++) {

                if (weight[i][j]!=INF){
                    edgeNum++;
                }
            }
        }
        return edgeNum;
    }

    //展示邻接矩阵
    public void showWeight(){

        for (int i = 0; i < weight.length; i++) {
            for (int j = 0; j < weight[0].length; j++) {
                System.out.printf("%12d",weight[i][j]);
            }
            System.out.println();
        }
    }
}

class 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 "EData{" +
                "start=" + start +
                ", end=" + end +
                ", weight=" + weight +
                '}';
    }
}

 

标签:10000,weight,23,int,graph,算法,prim,data,public
来源: https://blog.csdn.net/weixin_46133743/article/details/119485119

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有