图的最小生成树——普利姆算法

首先来介绍一下:图的最小树的概念
树是一种没有回路和环路的图
并且我们知道:只要我们对树结构进行遍历,就能够走遍树结构中的左右节点(比如二叉树的4种遍历方式)
并且,通过一张图所能够得到树结构的方案是有很多种的
那么在无向带权图中,我们为图中的每一条边都分配了权值
如果我们在删除一些边,去掉图中回路和环路之后保留下来的树结构中,所有节点之间边权的加和是所有方案中最小的
我们称这种树结构为无向图的最小生成树结构
最小生成树中保留了原有图中所有的节点,这些节点保存在同一个树结构当中,并且这个数的所有边权之和如其他树结构相比,取值最小
在数据结构所涉及的相关算法中,有两种算法可以用来计算无向图的最小生成树:普里姆(Prim)算法和克鲁斯卡尔(Kruskal)算法

普利姆算法
先画个示例图
在这里插入图片描述
步骤
步骤1:在所有的边中选择一个边权最小的边(如果具有相同最小权值的边有好几个,那么就随便选一个),使用这个边将两个节点连接起来,也就是将这个边加入生成树结构中
步骤2:将上述步骤中用到的点,存放在一个数组中,这个集合称之为“用过的点的集合”(一会儿我们要用这个集合来判断新选择的边是不是会构成回路)
步骤3:查找和所有已经用过的点相关的边,在这些边中挑出一个权值最小的边,加入生成树结构中,
但是在挑选的过程中我们需要保证:被选择的点,不能够出现在“用过的点的集合”中,因为如果出现这种情况,说明选择的边将使得图中出现回路,那就不是树结构了
步骤4:将这个距离最近的点,同样加入“用过的点的集合”
步骤5:重复步骤3-4,直到边的个数为节点的数量减一时候,说明所有的节点都已经加入最小生成树结构中了,程序终止
在这里插入图片描述

ABCDEF
A0615
B6053
C150564
D5502
E3606
F4260

实现的代码
图的临界矩阵表示

/**
 * 图的临界矩阵表示
 * @author 80769
 *
 */
public class MGraph {
	int size;//图中的节点个数
	char[] data;//存放的节点
	int[][] weight;//存放的边
	public MGraph(int size) {
		this.size=size;
		this.data=new char[size];
		weight=new int[size][size];
	}
}

实现最小生成树

/**
 * 创建图的邻接矩阵
 * 
 * @author xhh
 *
 */
public class MinTree {
	public void createGraph(MGraph graph, int size, char data[], int[][] weight) {
		for (int i = 0; i < size; i++) {
			graph.data[i] = data[i];
			for (int j = 0; j < size; j++) {
				graph.weight[i][j] = weight[i][j];
			}
		}
	}

	/**
	 * 普利姆算法的实现
	 * 
	 * @param graph 图的临界表
	 * @param node  起始顶点
	 */

	public int prim(MGraph graph, int node) {
		int sum=0;//权值总和
		int[] visited = new int[graph.size];// 创建一个数组,存放是否被访问过的节点
		for (int i = 0; i < graph.size; i++) { // 初始化数组
			visited[i] = 0;
		}
		visited[node]=1;
		int h1=-1;//边的弧尾
		int h2=-1;//边的头
		int minweight=Integer.MAX_VALUE; //最小权重
		for(int i=1;i<graph.size;i++) {  //size个顶点,最小生成树中有size-1条边
			for(int j=0;j<graph.size;j++) { //j顶点表示被访问过的顶点
				for(int k=0;k<graph.size;k++) { //k顶点表示被访问过的顶点
					if(visited[j]==1 && visited[k]==0 && graph.weight[j][k]<minweight && graph.weight[j][k]!=0) {
						//寻找已访问的顶点与未访问的定点间的权值最小的边
						minweight=graph.weight[j][k];
						h1=j;
						h2=k;
					}
				}
			}
			System.out.println("边<"+graph.data[h1]+","+graph.data[h2]+"> 权值:"+minweight);
			visited[h2]=1;
			sum+=minweight;
			minweight=Integer.MAX_VALUE;
		}
		return sum;
		
	}
	public static void main(String[] args) {
		int maxValue=Integer.MAX_VALUE;
		char[] data=new char[] {'A','B','C','D','E','F'};
		int size=data.length;
		int[][] weight=new int[][] {
			{0,6,1,5,maxValue,maxValue},
			{6,0,5,maxValue,3,maxValue},
			{1,5,0,5,6,4},
			{5,maxValue,5,0,maxValue,2},
			{maxValue,3,6,maxValue,0,6},
			{maxValue,maxValue,4,2,6,0}
		};
		MGraph mGraph = new MGraph(size);
		MinTree minTree=new MinTree();
		minTree.createGraph(mGraph, size, data, weight);
		int sum = minTree.prim(mGraph, 0);
		System.out.println("权值总和:"+sum);
		
	}

}

运行结果
在这里插入图片描述

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
普利算法是一种用于求解无向图最小生成树的基本算法。它以一个起始节点开始,每次将一个未被访问的节点加入到最小生成树集合中,并选择与该集合相邻的边中权重最小的边和它连接的未访问节点,直到最小生成树集合包含了中的所有节点。普利算法的时间复杂度为O(n^2)。 以下是普利算法的步骤: 1. 选择一个起始节点作为最小生成树的起点。 2. 将该起始节点加入最小生成树集合,并将其标记为已访问。 3. 在所有与最小生成树集合相邻的边中,选择权重最小的边和它连接的未访问节点。 4. 将该边和节点加入最小生成树集合,并将该节点标记为已访问。 5. 重复步骤3和步骤4,直到最小生成树集合包含了中的所有节点。 以下是Python实现普利算法的代码: ```python def prim(graph): # 选择一个起始节点作为最小生成树的起点 start_node = list(graph.keys())[0] # 将该起始节点加入最小生成树集合,并将其标记为已访问 visited = [start_node] # 初始化最小生成树的权重为0 min_weight = 0 # 初始化最小生成树的边为空 min_tree = [] # 重复步骤3和步骤4,直到最小生成树集合包含了中的所有节点 while len(visited) < len(graph): # 在所有与最小生成树集合相邻的边中,选择权重最小的边和它连接的未访问节点 min_edge = None for node in visited: for neighbor, weight in graph[node].items(): if neighbor not in visited: if min_edge is None or weight < min_edge[2]: min_edge = (node, neighbor, weight) # 将该边和节点加入最小生成树集合,并将该节点标记为已访问 visited.append(min_edge[1]) min_weight += min_edge[2] min_tree.append(min_edge) # 返回最小生成树的权重和边 return min_weight, min_tree ```

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值