数据结构_最小生成树_Prim_python/c
exmaple:
python code:
import math import logging as l l.basicConfig(level=l.INFO) inf = math.inf class Edge(): def __init__(self, start, end, weight): self.start = start self.end = end self.weight = weight class Node(): def __init__(self, sign): self.sign = sign self.key = math.inf self.precursor = None class G(): root=None def __init__(self, edges, nodes): self.edges = edges self.nodes = nodes def prim(self, root): l.debug(f"{root}") l.debug(f"root.key:{root.key}") G.root=root G.root.key = 0 A = [] Q = self.nodes[:] while len(Q) > 0: u = min(Q, key=lambda node: node.key) Q.remove(u) A.append(u) for edge in self.edges: v = edge.start if edge.end.sign == u.sign: if v in Q: if edge.weight < v.key: v.precursor = u v.key = edge.weight return A def print_MST(self, A): sum_weight = 0 for node in A: sum_weight += node.key if node.precursor==None: precursor=G.root.sign else: precursor=node.precursor.sign print(node.sign, "->",precursor ) print("the smallest weight:", sum_weight) # def print_MST(Q): # sum_weight=0 # for node in Q: # sum_weight+=node.key # print(sum) def get_node_instance(sign): for node in nodes: if node.sign == sign: return node # throw exception return None # get the edges parameters to instantiate the edge nodes ,put the edges to the list edges; def generate_edges(): while(True): line = input("input node:") if line == "0": break edge_param = line.split(",") start, end, weight = edge_param[0], edge_param[1], int(edge_param[2]) start_node = get_node_instance(start) end_node = get_node_instance(end) # print(end_node.sign) edges.append(Edge(start_node, end_node, weight)) return edges def generate_nodes(): # get the nodes number(you can custom the number regularity,there use the default simple number system) nodes = [Node(str(sign)) for sign in range(1, 8+1)] return nodes '''debug the edges is right: ''' def print_edges(): for edge in edges: # print(edge.start.sign,edge.end.sign,edge.weight) l.info((edge.start.sign, edge.end.sign, edge.weight)) nodes = [] nodes = generate_nodes() edges = [] edges = generate_edges() G = G(edges, nodes) # G.print_nodes() source_node = input("input the source node you want:(from 1~8)\n") # source_node="A" source_node = get_node_instance(source_node) result = G.prim(source_node) # print_MST(result) G.print_MST(result) ''' 1,2,6 2,1,6 1,3,5 3,1,5 2,3,12 3,2,12 3,4,9 4,3,9 3,6,7 6,3,7 6,5,15 5,6,15 6,7,10 7,6,10 1,7,8 7,1,8 1,8,14 8,1,14 7,8,3 8,7,3 0 3,8,5,6,7,9,15 '''
the input example and result:
understanding the prim algorithm:
#include "stdio.h" #include "stdlib.h" #define OK 1 #define ERROR 0 #define TRUE 1 #define FALSE 0 #define MAXEDGE 20 #define MAXVEX 20 #define Infinity 65535 typedef int Status; /* Status是函数的类型,其值是函数结果状态代码,如OK等 */ /*prime算法,基于邻接矩阵实现)*/ typedef struct MGraph { int arc[MAXVEX][MAXVEX]; int numVertexes, numEdges; }MGraph; void CreateMGraph(MGraph *G)/* 构建图 */ { int i, j; /* printf("请输入边数和顶点数:"); */ G->numEdges=15; G->numVertexes=9; /*初始化所有边(上的权值(代价))(分别是无穷大代价(绝大部分)和0代价*/ for (i = 0; i < G->numVertexes; i++)/* 初始化图 */ { for ( j = 0; j < G->numVertexes; j++) { if (i==j) G->arc[i][j]=0;/*自身到自身的距离(代价)为0*/ else G->arc[i][j] = G->arc[j][i] = Infinity;/*其余一律初始化为无穷代价*/ } } /*填写某些边的权*/ G->arc[0][1]=10; G->arc[0][5]=11; G->arc[1][2]=18; G->arc[1][8]=12; G->arc[1][6]=16; G->arc[2][8]=8; G->arc[2][3]=22; G->arc[3][8]=21; G->arc[3][6]=24; G->arc[3][7]=16; G->arc[3][4]=20; G->arc[4][7]=7; G->arc[4][5]=26; G->arc[5][6]=17; G->arc[6][7]=19; /*无向图的对称性,可以利用循环赋值处理另一半(的权)*/ for(i = 0; i < G->numVertexes; i++) { for(j = i; j < G->numVertexes; j++) { G->arc[j][i] =G->arc[i][j]; } } }//create()/*画各示意图,比什么都管用*/ /* Prim算法生成最小生成树(最小代价的极小连通子图) (参数为图G) */ void MiniSpanTree_Prim(MGraph G)/*画各示意图,比什么都管用*/ { int min, /*最小权值*/ i, j, k;/*最小权值所对应的顶点下标(从列号的角度); 弧的终点下标*/ /*定义两个辅助整型数组*/ int adjvex[MAXVEX]; /* (保存下标)保存相关顶点下标(作为顶点下标集合(严格来说,不是集合(其中的元素可以重复(重复次数为以该顶点为弧起点的边(弧)数)(最小生成树的各边的弧尾(起点)下标);adjvex[i]表示最小生成树的成员弧中弧终点为i号顶点的弧的起点顶点的下标 (最终是要被填满的!);注意adjvex[]不是图结构的成员 */ int lowcost[MAXVEX]; /* (保存权)保存相关顶点间 边的权值(最小代价集合)(弧的起点i(当前起点集中的(所有)点)到j)的弧上的最小代价者为lowcost[j];还要注意的是,lowcost[]的取值与代价矩阵的第一行仅仅只是初始化的关 系;*/ /*刚开始我们可能需要猜测辅助变量/数组的作用及含义,如果不对,及时修正即可,总之,宜把可能性写下.而且,我们可以利用调试工具以及终端的打印内容来监视程序尾声时数组里的值得分布情况分析含义(比如本程序adjvex[]数组的元素情况与打印的内容*/ /*初始化第0个位置的值:*/ lowcost[0] = 0;/* 初始化第一个权值为0,即v0加入生成树;注意,lowcost[]是独立于网图矩阵之外的独立数组,迭代收集的是整个矩阵中满足要求的数据 */ /* lowcost的值为0,在这里就是此下标的顶点已经加入生成树 */ adjvex[0] = 0; /* 初始化第一个顶点下标为0 */ /*初始化的循环,将lowcost[]初始化为代价矩阵第一行*/ for(i = 1; i < G.numVertexes; i++) /* 循环除下标为0外的全部顶点 */ { lowcost[i] = G.arc[0][i]; /* 将v0顶点与之有边的权值存入数组 (代价矩阵第一行);代价矩阵最初几乎都被初始化为无穷大(两个顶点间没有边,要过去的代价认为是无穷大的) 因此后期将找到点k为弧尾的最小生成树的边成员时,将lowcost[k]设为0表示k顶点任务已完成,后续的弧头(弧终点)不可能再是k顶点了) 一般默认通边的两点之间代价是>0的; 代价矩阵的元素作为右值*/ adjvex[i] = 0; /* 初始化都为v0的下标;(将所有弧的弧尾(弧的起点)初始化为下标0) 从后面的语句printf("(%d, %d)\n", adjvex[k], k);可看出,adjvex[k]就是终点为k号顶点的弧的起点(的下标值)*/ }//for /*两重双线循环(内部两个递进循环)*/ for(i = 1; i < G.numVertexes; i++)/*遍历所有顶点(除了0号),为了不漏掉某些点;循环变量i只是控制循环次数而已numVertexes-1次*/ { /*part_1 (包括打印结果)*/ min = Infinity; /* 初始化最小权(代价)值为∞;用于辅助找到要打印的k */ /* 通常设置为不可能的大数字如32767、65535等 */ j = 1;/*内层的循环变量,变化的比i快*/ k = 0;/*用于打印: 将最小代价值所对应的弧的弧头(终点)的下标存入k */ /*找到最小权值(从弧尾为0的顶点开始找)lowcost[]数组中的最小值记为min*/ while(j < G.numVertexes) /* 循环全部顶点 */ { /*找新终点(非零最小代价的)以便打印出来.lowcost[j]!=0;在所有lowcost[]的值全部为0前,总存在最小值!(因为prime()的参数必须时各连通图.)*/ if(lowcost[j]!=0 && lowcost[j] < min)/* 如果权值不为0且权值小于min */ { /*后头的part_2的操作可能使得lowcost[]出现介于0和无穷大的数*/ min = lowcost[j]; /* 则让当前权值成为最小值(权数组的元素只作为右值 */ k = j ; /* 将当前最小值的下标存入k */ }//if j++; }//while(j离开while后,总会达到G.numBertexes; /*输出最小生成树的所有弧(numVert-1)条(每条弧用顶点有序元组表示.)*/ printf("(%d, %d)\n", adjvex[k], k);/* 打印当前顶点(相关的)边中权值最小的边(构成边的两个顶点的下标) adjvex[0] = 0*/ lowcost[k] = 0;/* 将当前顶点的权值设置为0,表示此顶点已经完成任务,且已打印了 (此时以顶点k为弧(的终点)的任务结束了);lowcost[]被具体修改前,各元素值几乎都为无穷大(第0个位置以及后期初始create()中又指定权边的除外) 后面又用到lowcost[j] != 0的判断;*/ /*此时的k是上一条找到的最小生成树中的边弧成员的弧头(终点);在这里要成为新成员边的起点了*/ /*part_2*/ /*此时的k是上一条找到的最小生成树中的边弧成员的弧头(终点); 而k在这里要成为新成员边的起点了,下面的循环是为k顶点(新加入的弧起点)找到它的终点下标j;但这样合适的的终点可能并不存在,也就是说k不适合去链接其他顶点(无法为减少代价作出贡献);(由已处理的(其他起点)点去链接可使代价更小,反正只要全都连通即可) 已完成任务的点还是可以作为弧起点的嘛(虽然不能再作终点了);总之,顶点之间的链接谁做相对起点/做了多少次起点不重要,关键是,所有顶点都充当了终点且仅充当一次终点(0号可以不做终点)*/ for(j = 1; j < G.numVertexes; j++) /* 循环所有顶点 ,尽可能降低当前连通所有点所需要的代价.lowcost[k]=0只是表示连通到k的最小代价已经确定了,不需要再考虑连它的代价*/ {/*这一趟遍历使得当前已加入adjvex[]中的点到其他顶点的代价的最小值保存到对应的lowcost[]的相应位置(与弗洛伊德算法有些类似)*/ /*更新lowcost[]里的元素前还要作诸多判断*/ /*G.arc[k][j] < lowcost[j]这一部分你的确可以先找到代价矩阵中第k行的未被排除的所有权中的最小值,那到头来还是要和lowcost[]的对应元素比大小,还不如直接比 */ if(lowcost[j]!=0 && G.arc[k][j] < lowcost[j]) /*j顶点尚未完成任务;(后一句则是为了找到最合适(最小的代价的下标j)也可以作为内嵌if().*/ {/* 如果下标为k顶点各边权值小于此前这些顶点未被加入生成树权值 */ lowcost[j] = G.arc[k][j];/* 将较小的权值存入lowcost相应位置 */ adjvex[j] = k; /* 将以j号顶点为弧终点,(弧的起点)下标值k存入adjvex的j位置上;如果没有机会运行到此处,那么由初始化时的值0(号顶点)保留. 弧(adjvex(j) = k,j)*/ }//if }//for }//for }//MinispanPrime() int main(void) { MGraph G; CreateMGraph(&G); MiniSpanTree_Prim(G); return 0; }
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 分享4款.NET开源、免费、实用的商城系统
· 全程不用写代码,我用AI程序员写了一个飞机大战
· MongoDB 8.0这个新功能碉堡了,比商业数据库还牛
· 记一次.NET内存居高不下排查解决与启示
· 白话解读 Dapr 1.15:你的「微服务管家」又秀新绝活了
2021-05-19 7进制小数的有效位