精华内容
下载资源
问答
  • 数据结构课程设计 设计说明书 最小生成树普里姆算法的实现 学生姓名 学 号 班 级 成 绩 指导教师 数学与计算机科学学院 2013年3月15日 课程设计任务书 20122013学年第二学期 课程设计名称 数据结构课程设计 课程设计...
  • Prim算法能够在带权的图中搜索出最小生成树,这也是各大ACM和面试及考研题目中的热点,下面我们就来详细看一下Prim(普里姆)算法求最小生成树的思想及C语言实例讲解
  • 普里姆算法在找最小生成树时,将顶点分为两类,一类是在查找的过程中已经包含在树中的(假设为 A 类),剩下的是另一类(假设为 B 类)。 对于给定的连通网,起始状态全部顶点都归为 B 类。在找最小生成树时,选定...
  • 沈阳航空航天大学 课程设计报告 课程设计名称数据结构课程设计 课程设计题目Prim算法求最小生成树 院系专业: 院系 专 业: 班 级: 学 号: 姓 名: 指导教师: 计算机学院 计算机科学与技术物联网方向 扌旨导教师评语: ...
  • PAGE 2 数据结构课程设计 设计说明书 最小生成树普里姆算法的实现 学生姓名 学号 班级 计本 成绩 指导教师 计算机科学与技术系 数据结构 课程设计评阅书 题目 最小生成树普里姆算法的实现 学生姓名 学号 指导教师...
  • 最小生成树算法,基于Vs2010,可直接运行,代码可修改
  • 课程设计成果 学院: 计算机工程学院 班 级: 计算机科学与技术 学生姓名: 学 号: 设计地点 单位 设计题目: 普里姆算法求最小生成树 完成日期 2016年 1月6 日 指导教师评语_ _ _ _ _ _ 成绩(五级记分制_ 教师签名_ ...
  • 普里姆

    2019-03-23 20:56:50
    package Y2018; import java.util.ArrayList; import java.util.List; import java.util.Scanner; /* 题目的背景: John 的农场缺水!... 题目描述:农民 John 决定将水引入到他的 n(1≤n≤300)个牧场。...
    package Y2018;
    import java.util.ArrayList;
    import java.util.List;
    import java.util.Scanner;
    /*
    题目的背景:
    	John 的农场缺水!
    	题目描述:农民 John 决定将水引入到他的 n(1≤n≤300)个牧场。
    	他准备通过挖若干井,并在各块田中修筑水道来连通各块田地以供水。
    	在第 i 号田中挖一口井需要花费 W_(i 1≤W_i≤100000)元。
    	链接 i 号田与 j 号田需要P_ij(1≤P_ij≤100000,P_ji=P_ij)元。
    	请求出农民 John 需要为连通整个牧场的每一块田地所需要的钱数。
    
    输入格式:
    	第 1 行为一个整数 n
    	第 2 行到 n+1 行每行一个整数,从上到下分别为 W_i 到 W_n。
    	第 n+2 到 2n+1 行为一个矩阵,表示需要的经费(P_ij)。
    输出格式:
    	只有一行,为一个整数,表示所需要的钱数。
     */
    public class Main {
    	
    	private static int n;//农场数
    	private static int[] farm = new int[310];//农场费用
    	private static int[][] pipe = new int[310][310];//水管费用
    	
    	private static boolean[] used = new boolean[310];//默认false,记录已经用过的点
    	private static List<Integer> points = new ArrayList<>();//存入已经用过的点
    	
    	private static int minM = 0;//最小费用
    	
    	public static void main(String args[]) {
    		Scanner sn = new Scanner(System.in);
    		n = sn.nextInt();//这是农场个数
    		for(int i = 0;i < n;i++) {
    			farm[i] = sn.nextInt();//农场打井的费用
    		}
    		
    		for(int i = 0;i < n;i++) {
    			for(int j = 0;j < n;j++) {
    				pipe[i][j] = sn.nextInt();//农场与农场之间修水管的费用
    			}
    		}
    		points.add(-1);//将-1认为是虚拟点
    		for(int i = 0;i < n;i++)//将每一个点都遍历通过
    			prim();
    		System.out.println(minM);
    		sn.close();
    	}
    
    	private static void prim() {
    		int min = Integer.MAX_VALUE;//记录每一次的最小值
    		int pos = -2;//记录取最小值所涉及到的最小点
    		for(int i = 0;i < points.size();i++) {
    			int point = points.get(i);//循环遍历,选择起点
    			for(int j = 0;j < n;j++) {
    				if(!used[j]) {
    					if(point == -1 && farm[j] < min) {
    						min = farm[j];
    						pos = j;
    					}else if(point != -1 && pipe[point][j] < min) {
    						min = pipe[point][j];
    						pos = j;
    					}
    				}
    			}
    		}
    		minM += min;
    		used[pos] = true;
    		points.add(pos);
    		System.out.println(min+" "+pos);
    	}
    }
    

     

    展开全文
  • 求最小生成树的prim算法的C语言简单实现
  • 普里姆算法

    2012-12-09 19:49:45
    普里姆算法 // 123.cpp : 定义控制台应用程序的入口点。 // #include "stdafx.h" #include using namespace std; #define MAX 9999 typedef struct ArcNode
  • c++实现普里姆算法

    2018-11-28 16:06:11
    c++实现普里姆算法,已经用Dec c++测试过没有问题数据
  • JINGCHU UNIVERSITY OF TECHNOLOG Y 数据结构C语言描述? 课程设计 学 院 计算机工程学院 班 级 12级软件技术1班 学 号 2012304040122 120 124133121 学生姓名 周鑫王彬彬李松平 张圣玮魏远迎 扌指导教师 余云霞 2014...
  • 普里姆算法(prim)

    2021-10-23 11:59:36
    普里姆算法求最小生成树: 算法思路: 先从图中任选一个顶点并做标记,再比较与该顶点直接连接的所有顶点的权值,从中挑选最小权值相连,并将该顶点做标记,得到第一条路径。再从已标记的顶点的所有直接相连顶点且...

    算法背景:

     图中顶点A,B,C,D,E,F,G七个顶点表示七个村庄,现要修路,使得任意两个村庄之间都有通路,即都能到达(可以不是直达),问怎样修路可以使修路的总里程最短?

    思路:

    将所有边都连接起来,肯定都有通路,但总里程数一定不是最小。应该使用尽量少的路线,并使每条路线的长度最小,便可保证总里程最小。

    最小生成树:

    修路问题的本质就是最小生成树问题,最小生成树:Minimum Cost Spanning Tree,简称MST。具体为:给定一个带权的无向图,如何选取生成树,使得所有边上权的总和最小。

    最小生成树特征:

    • 有N个顶点,则N-1条边。
    • 包含了图的全部顶点。
    • N-1条边都在图中。

    普里姆算法求最小生成树:

    算法思路:

    先从图中任选一个顶点并做标记,再比较与该顶点直接连接的所有顶点的权值,从中挑选最小权值相连,并将该顶点做标记,得到第一条路径。再从已标记的顶点的所有直接相连顶点且未标记过的顶点中选取最小权值,再做标记,得到第二条路径。依次类推,直到变为一颗最小生成树。

    核心代码:

    for(int i = 1; i < graph.numOfVertex;i++) {//最小生成树边数
    	int v1 = -1;//已标记顶点
    	int v2 = -1;//未标记顶点
    	int tempmMin = Integer.MAX_VALUE;//当前最小权值
    	for(int j = 0; j < graph.numOfVertex;j++) {
    		for(int k = 0; k < graph.numOfVertex;k++) {
    			if(visited[j] == true && visited[k] == false && graph.edge[j][k] < tempmMin){
    				 v1 = j;//保存已标记和未标记顶点
                     v2 = k;
                     tempmMin = graph.edge[j][k];//保存权值
    			}
    		}
    	}
    	sum += tempmMin;//总权值之和
        //输出连接点信息
    	visited[v2] = true;//更新标记数组
    	tempmMin = Integer.MAX_VALUE;//更新当前最小权值
    }

    java代码:

    import java.util.*;
    
    public class Prim {
    	public static void main(String[] args) {
    		String[]vertex = {"A","B","C","D","E","F","G"};
    		int MAX = Integer.MAX_VALUE;
    		int [][]edge = new int[][] {
    			{MAX,5,7,MAX,MAX,MAX,2},
    			{5,MAX,MAX,9,MAX,MAX,3},
    			{7,MAX,MAX,MAX,8,MAX,MAX},
    			{MAX,9,MAX,MAX,MAX,4,MAX},
    			{MAX,MAX,8,MAX,MAX,5,4},
    			{MAX,MAX,MAX,4,5,MAX,6},
    			{2,3,MAX,MAX,4,6,MAX}
    		};
    		Graph graph = new Graph(7);
    		MST mst = new MST();
    		mst.createGraph(graph, vertex, edge);
    		mst.showGraph(graph);
    		System.out.println();
    		
    		mst.prim(graph, 0);
    	}
    }
    
    class MST{
    	public void createGraph(Graph graph,String []v,int[][]e) {
    		for(int i = 0; i < graph.numOfVertex;i++) {
    			graph.vertex.add(v[i]);
    			for(int j = 0; j < graph.numOfVertex;j++) {
    				graph.edge[i][j] = e[i][j];
    			}
    		}
    	}
    	public void showGraph(Graph graph) {
    		System.out.println("图的邻接矩阵表示为:");
    		for(int[] data : graph.edge) {
    			System.out.println(Arrays.toString(data));
    		}
    	}
    	public void prim(Graph graph,int start) {
    		boolean[]visited = new boolean[graph.numOfVertex];
    		visited[start] = true;
    		int sum = 0;
    		for(int i = 1; i < graph.numOfVertex;i++) {
    			int v1 = -1;
    			int v2 = -1;
    			int tempmMin = Integer.MAX_VALUE;
    			for(int j = 0; j < graph.numOfVertex;j++) {
    				for(int k = 0; k < graph.numOfVertex;k++) {
    					if(visited[j] == true && visited[k] == false && graph.edge[j][k] < tempmMin){
    						v1 = j;
    						v2 = k;
    						tempmMin = graph.edge[j][k];
    					}
    				}
    			}
    			sum += tempmMin;
    			System.out.printf("连接顶点%s和顶点%s,权值(距离)为%d\n",graph.vertex.get(v1),graph.vertex.get(v2),tempmMin);
    			visited[v2] = true;
    			tempmMin = Integer.MAX_VALUE;
    		}
    		System.out.printf("\n最佳修路长度为%d米",sum);
    	}
    }
    
    class Graph{
    	int numOfVertex;
    	ArrayList<String> vertex;
    	int [][]edge;
    	
    	public Graph(int numOfVertex) {
    		this.numOfVertex = numOfVertex;
    		vertex = new ArrayList<String>(numOfVertex);
    		edge = new int[numOfVertex][numOfVertex];
    	}
    }

    程序输出:

    图的邻接矩阵表示为:
    [2147483647, 5, 7, 2147483647, 2147483647, 2147483647, 2]
    [5, 2147483647, 2147483647, 9, 2147483647, 2147483647, 3]
    [7, 2147483647, 2147483647, 2147483647, 8, 2147483647, 2147483647]
    [2147483647, 9, 2147483647, 2147483647, 2147483647, 4, 2147483647]
    [2147483647, 2147483647, 8, 2147483647, 2147483647, 5, 4]
    [2147483647, 2147483647, 2147483647, 4, 5, 2147483647, 6]
    [2, 3, 2147483647, 2147483647, 4, 6, 2147483647]
    
    连接顶点A和顶点G,权值(距离)为2
    连接顶点G和顶点B,权值(距离)为3
    连接顶点G和顶点E,权值(距离)为4
    连接顶点E和顶点F,权值(距离)为5
    连接顶点F和顶点D,权值(距离)为4
    连接顶点A和顶点C,权值(距离)为7
    
    最佳修路长度为25米
    展开全文
  • 普里姆算法 普里姆算法(Prim算法),图论中的一种算法,可在加权连通图里搜索最小生成树。意即由此算法搜索到的边子集所构成的树中,不但包括了连通图里的所有顶点(英语:Vertex (graph theory)),且其所有边的...

    本文所有代码都基于Java实现图的存储和创建一文所实现的带权无向图

    普里姆算法

    普里姆算法(Prim算法),图论中的一种算法,可在加权连通图里搜索最小生成树。意即由此算法搜索到的边子集所构成的树中,不但包括了连通图里的所有顶点(英语:Vertex (graph theory)),且其所有边的权值之和亦为最小。

    Prim算法构造最小生成树的过程如下图所示:

    普里姆算法生成过程
    初始时从图中任取一顶点(如顶点A)加入树T,此时树中只有一个顶点,之后选择一个与当前T中顶点集合距离最近的顶点,并将该顶点和相应的边加入T,每次操作后T中的顶点数和边数都加1。以此类推,直至图中所有顶点都并入T,得到的就是最小生成树。初始T必有n-1条边。

    Prim算法的步骤如下:

    假设G={V,E}是连通图,其最小生成树T={U,Et},Et是最小生成树中边的集合。
    初始化: 向空树T={U,Et}中添加图G={V,E}的任一顶点u0,使U={u0},Et≠空集
    循环: (重复下列操作直至U=V),从图G中选择满足{(u,v)|u∈U,v∈U-V}且具有最小权值的边(u,v),加入树T,置U=U∪{v},Et = Et∪{(u,v)}

    简单来说就是先往空树中添加任一顶点,然后把这颗树以及图去掉被添加的顶点和边剩余的部分分别视为两部分,然后不断选取连接两部分且权值最小的边,直至所有顶点都被加入树中。

    代码实现

    public graph_t getMSTbYPrim(){
        //普里姆算法求最小生成树
        //初始化空树
        graph_t res = new graph_t();
        //从当前图的第一个顶点开始生成
        VNode curNode = this.vertices.get(0);
        res.addNode(new VNode(curNode.getName()));
        //初始化边集合
        ArrayList<ArcNode> arcList = new ArrayList<>();
        //将添加的顶点所连接的所有边添加进边集合,后序被添加的顶点也需要此操作
        ArcNode tmpArcNode = curNode.first;
        while (tmpArcNode != null){
            arcList.add(new ArcNode(tmpArcNode));
            tmpArcNode = tmpArcNode.next;
    
        }
        while (res.vertices.size() < this.vertices.size()){
            //选出符合条件的边
            ArcNode curArc = this.getMinimumSpanArc(res,arcList);
            //找到新加入的顶点,根据我的设计逻辑需要先添加顶点,再添加边
            //因为每条边在添加时都会被添加两次,若先添加边,则添加时另一
            // 个顶点还不在图中,导致邻接表中每条边只出现一次(应该出现两次)
            if (res.containVnode(curArc.node1.getName()) >= 0){
                curNode = curArc.node2;
            }else{
                curNode = curArc.node1;
            }
            //加入顶点
            res.addNode(new VNode(curNode.getName()));
            //将此边加入生成树
            res.addArc(new ArcNode(curArc));
            //将添加的顶点所连接的所有边添加进边集合,后序被添加的顶点也需要此操作
            tmpArcNode = curNode.first;
            while (tmpArcNode != null){
                arcList.add(new ArcNode(tmpArcNode));
                tmpArcNode = tmpArcNode.next;
            }
        }
        return res;
    }
    
    private ArcNode getMinimumSpanArc(graph_t g,ArrayList<ArcNode> arcList){
        //prim算法的辅助函数,从边集合中选出连接res和其余顶点的权值最小的边
        ArcNode res = new ArcNode(arcList.get(0));
        //将初始权重设为极大值,否则可能出现以下情况:
        //该边不满足连接条件但是其权值比所有满足连接条件的边都小,从而导致无法找到正确的边
        res.setWeight(Integer.MAX_VALUE);
        for (ArcNode arc:arcList) {
            //两种情况,满足其一即满足连接两边的条件
            arc = new ArcNode(arc);
            boolean linked1 = g.containVnode(arc.node1.getName()) >= 0 && g.containVnode(arc.node2.getName()) < 0;
            boolean linked2 = g.containVnode(arc.node2.getName()) >= 0 && g.containVnode(arc.node1.getName()) < 0;
            if((linked1||linked2)&&arc.getWeight() < res.getWeight()){
                res = arc;
            }
        }
        //找到的同时要将此边从边集合中去除
        arcList.remove(res);
        //处理该边,使其不与其他边有联系
        res.next = null;
        return res;
    }
    

    代码主要涉及两个方法,其中public graph_t getMSTbYPrim()是Prim算法的主方法,另外一个方法private ArcNode getMinimumSpanArc(graph_t g,ArrayList<ArcNode> arcList)用来从边集合中选取符合条件的边,每次被选中的都是将被加入生成树的边。

    克鲁斯卡尔算法

    与Prim算法从顶点开始扩展最小生成树不同,Kruskal(克鲁斯卡尔)算法是一种按权值的递增次序选择合适的边来构造最小生成树的方法。

    克鲁斯卡尔算法构造最小生成树的过程如下图所示:
    克鲁斯卡尔算法构造过程
    初始时为只有n个顶点而无边的非连通图T={V,{}},每个顶点自成一个连通分量,然后按照边的权值由小到大的顺序,不断选取当前未被选取且权值最小的边若该边依附的顶点落在T中不同的联通分量上,则将此边加入T,否则舍弃此边而选择下一条权值最小的边。以此类推,直至T中所有顶点都在一个联通分量上。

    Kruskal算法的步骤如下:

    假设G={V,E}是连通图,其最小生成树T={U,Et}
    初始化: U = V, Et = 空集。即每个顶点构成一棵独立的树,T此时是一个仅含|V|个顶点的森林。
    循环 (重复下列操作直至T是一棵树): 按G的边的权值递增顺序依次从E-Et中选择一条边,若这条边加入T后不构成回路,则将其加入Et,否则舍弃,知道Et中含有n-1条边。

    代码实现

    public graph_t getMSTByKruskal(){
            //克鲁斯卡尔算法求最小生成树
            //初始化生成树
            graph_t res = new graph_t();
            //初始化边集合
            ArrayList<ArcNode> arcList = new ArrayList<>();
            //将图的所有边加入边集合
            for (VNode vnode:this.vertices) {
                ArcNode tmpArc = new ArcNode(vnode.first);
                while (tmpArc != null){
                    arcList.add(tmpArc);
                    tmpArc = tmpArc.next;
                }
            }
            while (res.getVexNum() < this.getVexNum() || res.getConnNum() > 1){
                //当生成树中顶点数小于图中定点数或者生成树还不是一个连通图时说明生成还未完成
                //选出满足条件的权值最小的边
                ArcNode  selectedArc = getArcForKruskal(res,arcList);
                if(res.containVnode(selectedArc.node1.getName()) < 0){
                    //若顶点1未被连接则添加该顶点
                    res.addNode(new VNode(selectedArc.node1.getName()));
                }
                if(res.containVnode(selectedArc.node2.getName()) < 0){
                    //若顶点2未被连接则添加该顶点
                    res.addNode(new VNode(selectedArc.node2.getName()));
                }
                //添加该边
                res.addArc(new ArcNode(selectedArc));
            }
            return res;
        }
    
    private ArcNode getArcForKruskal(graph_t g,ArrayList<ArcNode> arcList){
        //克鲁斯卡尔算法的辅助函数,从边集合中选出符合条件的边
        ArcNode res = new ArcNode(arcList.get(0));
        res.setWeight(Integer.MAX_VALUE);
        for (ArcNode arc:arcList) {
            arc = new ArcNode(arc);
            //若顶点1已被连接
            boolean linked1 = g.containVnode(arc.node1.getName()) >= 0 ;
            //若顶点2已被连接
            boolean linked2 = g.containVnode(arc.node2.getName()) >= 0 ;
            if(((!(linked1&&linked2))||(!g.canReach(arc.node1,arc.node2)))&&arc.getWeight() < res.getWeight()){
                //只要不是两个顶点都已被连接或者二者不属于同一联通分量就满足连接条件
                res = arc;
            }
        }
        //找到的同时要将此边从边集合中去除
        arcList.remove(res);
        //处理该边,使其不与其他边有联系
        res.next = null;
        //不这样处理的话,该边所引用的两个顶点还是之前图中的顶点
        res.setNode1(new VNode(res.node1.getName()));
        res.setNode2(new VNode(res.node2.getName()));
        return res;
    }
    public boolean canReach(VNode node1,VNode node2){
        //通过dfs判断从node1出发是否能到达node2,如果能则二者属于同一连通分量,否则不属于
        //该方法与BFS思路一样只是将队列改为栈
        //访问标记数组初始化
        graph_t.visited = new boolean[this.vexNum];
        for (int i = 0; i < this.vexNum; i++) {
            visited[i] = false;
        }
        Stack<VNode> stack = new Stack<>();
        stack.push(this.vertices.get(this.containVnode(node1.getName())));
        while (!stack.isEmpty()){
            VNode node = stack.pop();
            int curIndex = this.containVnode(node.getName());
            if (!graph_t.visited[curIndex]){
    //                tmpRes.add(node.getName());
                graph_t.visited[curIndex] = true;
                //如果node后面没有边,直接调用new ArcNode(node.first)会导致空指针异常
                ArcNode tmpArcNode = node.first == null ? null : new ArcNode(node.first);
                while (tmpArcNode != null){
                    if (node.getName().equals(tmpArcNode.getNode1().getName())){
                        //此时node2试邻接点
                        stack.push(this.getVNodeByName(tmpArcNode.getNode2().getName()));
                    }else{
                        //否则node1为邻接点
                        stack.push(this.getVNodeByName(tmpArcNode.getNode1().getName()));
                    }
                    tmpArcNode = tmpArcNode.next;
                }
            }
        }
        return graph_t.visited[this.containVnode(node2.getName())];
    }
    public int getConnNum(){
        //通过获取图的连通分量个数来判断图是否为来连通图
        //通过改造DFSTraverse方法来获取连通分量个数,DFS函数执行的次数对应着连通分量个数
        graph_t.visited = new boolean[this.vexNum];
        for (int i = 0; i < this.vexNum; i++) {
            visited[i] = false;
        }
        ArrayList<String> res = new ArrayList<>();
        int connNum = 0;
        for (int i = 0; i < this.vexNum; i++) {
            if(!graph_t.visited[i]){
                res =  this.DFS(i,res);
                //每执行一个该函数,连通分量个数加一
                connNum++;
            }
        }
        return connNum;
    }
    

    克鲁斯卡尔算法虽然逻辑上比普里姆算法更加简单清晰,但其实现起来要比普里姆算法稍难一些,主要是因为克鲁斯卡尔算法在选择边时需要判断两个顶点是否属于同一连通分量,于是我又额外添加了两个方法public int getConnNum()private ArcNode getArcForKruskal(graph_t g,ArrayList<ArcNode> arcList),其中前者用来计算一个图中共有几个连通分量,借助它可以判断算法何时结束,后者用来判断两个顶点是否属于同一连通分量,这两个方法的实现思路都已写在注释里了。

    运行效果

    测试运行的代码:

    public static void main(String[] args) {
        graph_t g = new graph_t();
        g.create();
        g.showGraph();
        System.out.println("---------------克鲁斯卡尔算法-----------------------");
        graph_t MST = g.getMSTByKruskal();
        MST.showGraph();
        System.out.println("---------------普里姆算法-----------------------");
        graph_t mst_p = g.getMSTbYPrim();
        mst_p.showGraph();
    }
    

    输入输出

    输入图即为上面描述两种算法构造过程的图片中的带权无向图
    输出结果如下:
    在这里插入图片描述
    在这里插入图片描述

    如有错误恳请指正

    展开全文
  • 1. 普里姆算法的原理 1.1. 应用场景-修路问题 1.2. 修路问题的思路分析 1.3. 修路问题的本质分析 1.3.1. 最小生成树基本介绍 1.4. 普里姆算法的基本介绍 1.5. 普里姆算法的步骤详解 1.6. 普里姆算法的步骤图解 2. ...
     
     
    

    博主的 Github 地址


    1. 普里姆算法的原理

    1.1. 应用场景-修路问题

    • 如下图所示的场景以及下面列出的问题
      pic
    1. 某地有 7 个村庄 [A, B, C, D, E, F, G], 现需要修路将 7 个村庄连通.

    2. 个个村庄的距离用边表示(权), 如 A-B 距离 5 公里.

    3. 如何修路保证各个村庄都能连通, 并且总的修建公路总里程最短?

    1.2. 修路问题的思路分析

    • 如果直接将 10 条边都连接起来, 虽然能保证村庄连通,
      但得到的总里程数并不是最小的, 因此这种方式不可行.

    • 正确的思路应该是尽可能少地选择路线, 并且每条路线的长度最小,
      在保证连通的前提下, 让每条路线的长度加起来的总里程数最小.

    1.3. 修路问题的本质分析

    修路问题实际是最小生成树问题, 最小生成树(Minimum Cost Spanning Tree), 简称 MST.

    1.3.1. 最小生成树基本介绍

    pic

    • 给定一个带权的无向连通图, 通过选取一棵生成树,
      使树上所有边的权值总和最小, 这就是最小生成树.

    • MST 如果有 N 个顶点, 那么一定会有 N-1 条边.

    • MST 包含了完全图的全部顶点, N-1 条边都可在完全图中找到.

    • 获取最小生成树的算法主要是普里姆算法和克鲁斯卡尔算法.

    1.4. 普里姆算法的基本介绍

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

    1.5. 普里姆算法的步骤详解

    1. G={V, E} 是连通网, T={U, D} 是最小生成树,
      其中 V 和 U 是顶点集合, 而 E 和 D 是边的集合.

    2. 若从顶点 u[i] 开始构造最小生成树,
      则从集合 V 中取出顶点 u[i] 放入集合 U 中,
      标记顶点 v 的 visited[u[i]]=1.

    3. 若集合 U 中顶点 u[i] 与集合 V-U 中的顶点 v[j] 之间存在边,
      则寻找这些边中权值最小的边, 但不能构成回路(即起点终点都是同一点),
      将顶点 v[j] 放入集合 U 中, 将边 (u[i], v[j]) 加入集合 D 中,
      标记 visited[v[j]]=1.

    4. 重复步骤 2, 直到 U 和 V 相等, 即所有顶点都标记为已访问,
      此时 D 集合中存在着 n-1 条边.

    1.6. 普里姆算法的步骤图解

    pic

    • 假设从点 A 开始构造 MST:

      • 将 A 先放入集合 U 中, 此时集合 U = {A};
      • 能跟 A 直接连通的顶点如下: A-C[7], A-G[2], A-B[5];
      • 其中 A-G[2] 的权值最小, 将 G 放入集合 U 中, U = {A, G}.
    • U = {A, G} 为子图继续构造:

      • 将 A 和 G 顶点和它们相邻还没进行访问的顶点进行处理;
      • A 顶点未访问的顶点为 A-C[7], A-B[5];
      • G 顶点未访问的顶点为 G-B[3], G-E[4], G-F[6];
      • 其中 G-B[3] 权值最小, 将 B 放入 U 中, U = {A, G, B}.
    • U = {A, G, B} 为子图继续构造:

      • 将 A 和 G, B 顶点和它们相邻还没进行访问的顶点进行处理;
      • A 顶点未访问的顶点为 A-C[7];
      • G 顶点未访问的顶点为 G-E[4], G-F[6];
      • B 顶点未访问的顶点为 B-D[9];
      • 其中 G-E[4] 权值最小, 将 E 放入 U 中, U = {A, G, B, E}.
    • U = {A, G, B, E} 为子图继续构造:

      • 将 A 和 G, B, E 顶点和它们相邻还没进行访问的顶点进行处理;
      • A 顶点未访问的顶点为 A-C[7];
      • G 顶点未访问的顶点为 G-F[6];
      • B 顶点未访问的顶点为 B-D[9];
      • E 顶点未访问的顶点为 E-C[8], E-F[5];
      • 其中 E-F[5] 权值最小, 将 F 放入 U 中, U = {A, G, B, E, F}.
    • U = {A, G, B, E, F} 为子图继续构造:

      • 将 A 和 G, B, E 顶点和它们相邻还没进行访问的顶点进行处理;
      • A 顶点未访问的顶点为 A-C[7];
      • G 顶点未访问的顶点为 null;
      • B 顶点未访问的顶点为 B-D[9];
      • E 顶点未访问的顶点为 E-C[8];
      • F 顶点未访问的顶点为 F-D[4];
      • 其中 F-D[4] 权值最小, 将 D 放入 U 中, U = {A, G, B, E, F, D}.
    • U = {A, G, B, E, F, D} 为子图继续构造:

      • 将 A 和 G, B, E 顶点和它们相邻还没进行访问的顶点进行处理;
      • A 顶点未访问的顶点为 A-C[7];
      • G 顶点未访问的顶点为 null;
      • B 顶点未访问的顶点为 null;
      • E 顶点未访问的顶点为 E-C[8];
      • F 顶点未访问的顶点为 null;
      • D 顶点未访问的顶点为 null;
      • 其中 A-C[7] 权值最小, 将 C 放入 U 中, U = {A, G, B, E, F, D, C}.
    • 至此 U {A, G, B, E, F, D, C} = V {A, B, C, D, E, F, G},
      结束构造过程, 算法结束.

    • 最终得出的结果如下图所示, 红线为选择的通路, 总路程为 25.
      pic

    2. 普里姆算法解决最小生成树问题的代码实现

    • 实现细节请看注释

    2.1. 图类

    package com.leo9.dc37.prim_algorithm;
    
    import java.util.Arrays;
    
    //定义图类
    public class MapGraph {
        //定义图的结点个数
        int vertex_num;
        //存放结点的数据
        char[] vertex_data;
        //邻接矩阵存放权值
        int[][] side_weight;
    
        //定义构造器, 初始化上述内容
        public MapGraph(int vertex_num) {
            this.vertex_num = vertex_num;
            vertex_data = new char[vertex_num];
            side_weight = new int[vertex_num][vertex_num];
        }
    
        /**
         * 初始化图的邻接矩阵
         *
         * @param map_graph   图对象
         * @param vertex_num  图的顶点个数
         * @param vertex_data 图的顶点值
         * @param side_weight 图的邻接矩阵
         */
        public void initMapGraph(MapGraph map_graph, int vertex_num, char[] vertex_data, int[][] side_weight) {
            int i, j;
            for (i = 0; i < vertex_num; i++) {
                map_graph.vertex_data[i] = vertex_data[i];
                for (j = 0; j < vertex_num; j++) {
                    map_graph.side_weight[i][j] = side_weight[i][j];
                }
            }
        }
    
        //显示图的邻接矩阵
        public void showGraph() {
            for (int[] row : this.side_weight) {
                System.out.println(Arrays.toString(row));
            }
        }
    }
    
    

    2.2. 普里姆算法类

    package com.leo9.dc37.prim_algorithm;
    
    import java.util.Arrays;
    
    public class PrimAlgorithm {
        public static void main(String[] args) {
            //定义顶点数组
            char[] vertex_data = {'A', 'B', 'C', 'D', 'E', 'F', 'G'};
            int vertex_num = vertex_data.length;
            //邻接矩阵的关系用二维数组表示, 10000表示两点间不连通
            //因为权值很大的话, 就不会选择到这条边作为通路.
            int[][] side_weight = new int[][]{
                    {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}
            };
    
            //创建一个图对象, 并进行初始化
            MapGraph map_graph = new MapGraph(vertex_num);
            map_graph.initMapGraph(map_graph, vertex_num, vertex_data, side_weight);
    
            //打印初始的图
            System.out.println("==============target graph===============");
            map_graph.showGraph();
    
            //打印最小生成树相关信息, 从数组下标为0的点开始
            getMST(map_graph, 0);
        }
    
        /**
         * 编写普里姆算法,得到最小生成树
         *
         * @param map_graph    图对象
         * @param vertex_start 表示从第几个顶点开始生成 MST
         */
        public static void getMST(MapGraph map_graph, int vertex_start) {
            //定义数组, 标记结点是否被访问过, 默认元素值为 0, 表示没访问过
            int[] vertex_visited = new int[map_graph.vertex_num];
    
            //定义最小生成树的邻接矩阵, 用来存储边的权值, 通路为 1, 不通为 0
            int[][] min_tree = new int[map_graph.vertex_num][map_graph.vertex_num];
    
            //定义最短路径长度, 用来计算最小生成树所有通路的权值之和
            int min_route = 0;
    
            //将当前结点标记为已访问
            vertex_visited[vertex_start] = 1;
    
            //h1 和 h2 记录两个顶点的下标
            int h1 = -1;
            int h2 = -1;
    
            //将最小值初始化一个较大的数, 后面遍历的时候会被替换
            int min_weight = 10000;
    
            System.out.println("==============the chosen sides===============");
            //开始循环遍历, 因为最小连通子图的边数为 vertex_num-1
            //因此 k 初始值为 1, 遍历结束后就能获取顶点数减一条边
            for (int k = 1; k < map_graph.vertex_num; k++) {
                //寻找已被访问过的顶点与未被访问的顶点的连线的权值中的最小值
                //即选出当前已访问的顶点与未访问的顶点中的最小边
                //因为给定的邻接矩阵是完全图, 不连通的点之间的边的权值用大数值表示, 因此不会影响判断最小值
                for (int i = 0; i < map_graph.vertex_num; i++) {
                    for (int j = 0; j < map_graph.vertex_num; j++) {
                        //通过遍历比较找到每一轮的最小边, 获取是哪两个顶点
                        //h1用来记录被访问顶点, h2用来记录未访问顶点
                        if (vertex_visited[i] == 1
                                && vertex_visited[j] == 0
                                && map_graph.side_weight[i][j] < min_weight) {
                            //找到最小边后替换最小值, 同时更新点的下标
                            min_weight = map_graph.side_weight[i][j];
                            h1 = i;
                            h2 = j;
                        }
                    }
                }
                //中间的双重循环结束后能找出第 k 条边, 存储相应的数据
                //最小生成树的邻接矩阵记录边的权值, 因为是无向图, 所以两个方向的边权值都记为 1
                min_tree[h1][h2] = 1;
                min_tree[h2][h1] = 1;
                //最短路径长度进行累加
                min_route += map_graph.side_weight[h1][h2];
                //本轮新增边的另一个未被访问的端点记录已访问
                vertex_visited[h2] = 1;
                //输出边
                System.out.printf("the side <%c,%c> is chosen, length = [%d]\n", map_graph.vertex_data[h1], map_graph.vertex_data[h2], map_graph.side_weight[h1][h2]);
                //min_weight 重置, 防止后续对比出错
                min_weight = 10000;
            }
            //输出最小生成树
            System.out.println("==============the min tree===============");
            for (int[] row : min_tree) {
                System.out.println(Arrays.toString(row));
            }
    
            //输出最短路径
            System.out.println("==============the min route===============");
            System.out.printf("the min route is [%d]\n", min_route);
        }
    }
    
    

    2.3. 测试结果

    • 显而易见, 最短路径长度是 25, 结果正确
      pic
    展开全文
  • 深度学习普里姆算法(Prim)

    千次阅读 多人点赞 2020-04-21 10:36:02
    如果你对数据结构了解不深,又想要学习普里姆算法,建议你看看我这篇,我尽量往细致的方向写,如果有些大佬认为我写的比较啰嗦,请见谅,毕竟我的目的是为了让数据结构小白能够在理解普里姆算法上相对容易一些。...
  • 普里姆算法最小生成树What to Learn? 学什么? How to construct minimum spanning tree using Prim's Minimum Spanning Tree algorithm and its C++ implementation? 如何使用Prim的最小生成树算法及其C ++实现...
  • 普里姆算法详解

    千次阅读 2020-08-13 12:26:58
    普里姆算法介绍 1)普利姆(Prim)算法求最小生成树,也就是在包含n个顶点的连通图中,找出只有(n-1)条边包含所有n个顶点的连通子图,也就是所谓的极小连通子图 2)普利姆的算法如下: (1)设G=(V,E)是连通网,T=(U,D)是...
  • 这里运用邻接矩阵来进行对普里姆算法的实现。 还是按照课本上的例子: 代码如下: #include <stdio.h> #include <stdlib.h> #include <string.h> #define MaxInt 32767 #define MVNum 100 //图的...
  • 文章目录一、最小生成树概述1.1 背景概述1.2 最小生成树的定义1.3 最小生成树构造算法二、普里姆算法2.1 普利姆算法介绍2.2 普利姆算法代码实现三、克鲁斯卡尔算法3.1 克鲁斯卡尔算法介绍3.2 克鲁斯卡尔算法代码实现...
  • 【数据结构基础整理】图--05:普里姆算法详解

    万次阅读 多人点赞 2020-02-17 13:43:40
    详解最小生成树的普里姆算法 0x01.关于普里姆算法 普里姆算法(Prim算法),图论中的一种算法,可在加权连通图里搜索最小生成树。意即由此算法搜索到的边子集所构成的树中,不但包括了连通图里的所有顶点(英语:...
  • 最小生成树算法之Prim(普里姆)算法

    千次阅读 2021-07-20 17:13:08
    最小生成树的可以通过Kruskal(克鲁斯卡尔)算法或Prim(普里姆)算法求出。 Prim算法基本介绍: Prim算法又称为"加点法",每次找出距离(此处的距离指的是距离最小生成树的距离,若此处无法理解,可直接跳过,看完下面...
  • 普里姆算法是从某一顶点为起点,逐步找各个顶点最小权值的边来构成最小生成树。那我们也可以直接从边出发,寻找权值最小的边来构建最小生成树。不过在构建的过程中要考虑是否会形成环的情况 边集数组存储图 ...
  • 克鲁斯卡尔算法(Kruskal): 设有一个有n个顶点的连通网N={V,E},最初先构造一个只有n个...普里姆算法是另一种构造最小生成树的算法,它是按逐个将顶点连通的方式来构造最小生成树的。 从连通网络 N = { V, E }中的某
  • 普里姆Prim构造最小生成树 一、最小生成树的概念 一个连通图的生成树是一个极小的连通子图,它含有图中的全部顶点,但只有构成一棵树的n-1条边。 对于一个带权(假定每条边上的权均大于0的数)连通无向图G中的不同...
  • 一、普里姆算法介绍 普里姆(Prim)算法,是用来求加权连通图的最小生成树的算法。 基本思想:对于图G而言,V是所有顶点的集合;现在,设置两个新的集合U和T,其中U用于存放G的最小生成树中的顶点,T存放G的最小生成树...
  • 普里姆算法(修路问题)+图解

    万次阅读 2021-04-05 13:26:12
    k++) {//因为有graph.verxs个顶点,普里姆算法结束后,有graph.verxs-1边 //这个确定每一次生成的子图,和哪个节点的距离最近 for (int i = 0; i ; i++) {//i节点表示被访问过的节点 for (int j = 0; j ; j++) {//j...
  • java实现普里姆算法

    2019-10-18 10:35:18
    求最小生成树的算法主要是普里姆算法和克鲁斯卡尔算法 三、普里姆算法介绍 普利姆(Prim)算法求最小生成树,也就是在包含 n 个顶点的连通图中,找出只有(n-1)条边包含所有 n 个顶点的 连通子图,也就是所谓的...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 4,716
精华内容 1,886
热门标签
关键字:

普里姆