Java教程

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

本文主要是介绍数据结构与算法---23.prim普利姆算法、kruskal克鲁斯卡尔算法,对大家解决编程问题具有一定的参考价值,需要的程序猿们随着小编来一起学习吧!

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 +
                '}';
    }
}

 

这篇关于数据结构与算法---23.prim普利姆算法、kruskal克鲁斯卡尔算法的文章就介绍到这儿,希望我们推荐的文章对大家有所帮助,也希望大家多多支持为之网!