图论基础知识--最小生成树算法kruskal(克鲁斯克尔)和普里姆算法(Prim算法);最短路径算法Dijkstra(迪杰斯特拉)和Floyd(弗洛伊德)
一.基礎知識
?
? ? ? ? ?有向圖? ?無向圖
以無向圖為例:
鄰接矩陣:
度矩陣(對角矩陣):
二.最小生成樹
應用:將網絡頂點看著城市,邊看著城市之間通訊網,邊的權重看著成本,根據最小生成樹可以構建城市之間成本最低的通訊網.
1.kruskal(克魯斯克爾)算法
2.普里姆算法(Prim算法)
求點與點之間的最小生成樹
代碼:
#coding:utf-8 """ 最小生成樹 """ import networkx as nx import matplotlib.pyplot as plt import numpy as np from numpy import randomG = nx.Graph() # Matrix = np.array(random.randint((5), size=(5, 5))) # print('==Matrix:', Matrix) # print(maps) Matrix = np.array( [[3, 4, 0, 2, 2],[4, 1, 0, 3, 4],[0, 0, 0, 4, 4],[2, 3, 4, 0, 3],[2, 4, 4, 3, 1]]) #實際在用的時候,只用了下三角矩陣 #構建無向圖 for i in range(len(Matrix)):for j in range(len(Matrix)):if Matrix[i, j] != 0:G.add_edge(i, j) nx.draw_networkx(G) plt.title("G") plt.show()class Graph(object):def __init__(self, Matrix):self.Matrix = Matrixself.nodenum = self.get_nodenum()self.edgenum = self.get_edgenum()def get_nodenum(self):return len(self.Matrix)def get_edgenum(self):count = 0for i in range(self.nodenum): # 獲取除去對角的下三角矩陣for j in range(i):# print('i,j', i, j)if self.Matrix[i][j] > 0 and self.Matrix[i][j] < 9999:count += 1return countdef kruskal(self):list = []if self.nodenum <= 0 or self.edgenum < self.nodenum - 1:return listedge_list = []for i in range(self.nodenum): # 獲取除去對角的下三角矩陣for j in range(i):if self.Matrix[i][j] < 9999:edge_list.append([i, j, self.Matrix[i][j]])print('==排序之前邊的集合 edge_list:', edge_list)edge_list.sort(key=lambda a: a[2]) # 已經排好序的邊集合print('==排序以后邊的集合 edge_list:', edge_list)group = [[i] for i in range(self.nodenum)] # 存儲代表元列表print('存儲代表元列表', group)for edge in edge_list:for i in range(len(group)):if edge[0] in group[i]:m = i#開始節點if edge[1] in group[i]:n = i#終止節點if m != n:# 合并聯通分量 進行存儲元列表更新list.append(edge)print('開始節點m,終止節點n:', m, n)group[m] = group[m] + group[n]group[n] = []print('==更新后的代表元列表:', group)return listdef prim(self):list = []if self.nodenum <= 0 or self.edgenum < self.nodenum - 1:return listselected_node = [0]candidate_node = [i for i in range(1, self.nodenum)]#候選節點# print('==candidate_node:', candidate_node)while len(candidate_node) > 0:begin, end, minweight = 0, 0, 9999for i in selected_node:for j in candidate_node:if self.Matrix[i][j] < minweight:minweight = self.Matrix[i][j]begin = i#存儲開始節點end = j#存儲終止節點list.append([begin, end, minweight])selected_node.append(end)#找到權重最小的邊 加入可選節點candidate_node.remove(end)#候選節點被找到 進行移除return list # # G = Graph(Matrix) print('鄰接矩陣為\n%s' % G.Matrix) print('節點數據為%d,邊數為%d\n' % (G.nodenum, G.edgenum)) print('------最小生成樹kruskal算法------') print(G.kruskal())print('------最小生成樹prim算法') print(G.prim())?
三.最短路徑:
1.Dijkstra(迪杰斯特拉)算法
Dijkstra算法用于解決單源最短路問題,所謂單源最短路就是指定一個起點,求出這個起點到其它所有點的最短路。本質就是依次讓每個頂點作為起點,更新最短路的過程。
示例:求M到各個頂點的最短路徑
? ? ? ? ? ? ?
先構建鄰接矩陣Adjacent, 初始狀態dist[1~n] = inf, dist[M]=0,頂點更新狀態vst[1~n] = 0
以點M為起點:
dist[M] = 0 vst[M] = 0 dist[W] = inf vst[W] = 0 dist[E] = inf vst[E] = 0 dist[D] = inf vst[D] = 0 dist[X] = inf vst[X] = 0找出dist中值最小且未被使用的點,發現dist[M]=0最小,且vst[M]=0,未被使用,故將M作為新的起點.
找出所有M可達的頂點,為X、W、E
dist[X]=inf > 0+10 ,更新dist[X]=10
dist[W]=inf > 0+5 ,更新dist[W]=5
dist[E]=inf > 0+8 ,更新dist[E]=8
M已被使用,vst[M]=1
依次遍歷每個頂點........?
Inf = float('inf') # Dijkstra算法,就是依次讓每個頂點作為起點,更新最短路的過程。 Adjacent = [[0, 10, 5, Inf, 8],[10, 0, 3, 1, Inf],[5, 3, 0, 9, 2],[Inf, 1, 9, 0, 6],[8, Inf, 2, 6, 0]]Src, Dst, N = 0, 4, 5def dijstra(adj, src, dst, n):dist = [Inf] * n # 初始化為inf無窮大dist[src] = 0 # 起始點到起始點距離為0vst = [0] * n # 記錄已經確定的頂點prev = [0] * nwhile True:now = -1for u in range(n): # 找到dist最小且vst=0的點作為起點if not vst[u] and (now == -1 or dist[u] < dist[now]):now = uprint('====now:', now)if now == -1: # now未被更新,即表示所有頂點都被使用過,算法結束breakfor v in range(n): # 遍歷當前起點now能到達的所有點if dist[v] > dist[now] + adj[now][v]: # 如果dist[v]大于dist[now] + adj[now][v] 則更新dist[v] = dist[now] + adj[now][v]prev[v] = nowprint('==dist:', dist)# assert 1==0vst[now] = 1 # 當前起點now已被使用過,vst[now]=1# print('===dist:', dist)# print('==prev:', prev)## print('==dist[dst]:', dist[dst])return dist, prevdist, prev = dijstra(Adjacent, Src, Dst, N)print('==dist,prev:', dist, prev)def construct_path(prev, index, path=[]):path = path + [prev[index]]if prev[index] == 0:#終止條件return pathnew_path = construct_path(prev, prev[index], path)return new_path # res = construct_path(prev, 4,[4]) # print('==res:', res) for i in range(len(prev)):path = construct_path(prev, i, [i])print('{}節點路徑為:{}'.format(i, path))2.Floyd(弗洛伊德)
本質任意兩個節點最短路徑是否經過此節點,用dp的思想來存儲中間結果.
示例:
#Floyd(弗洛伊德)找最短路徑 本質任意兩個節點最短路徑是否經過此節點 import numpy as np Inf = float('inf') DIS = [[0, 3, 8, Inf, -4],[Inf, 0, Inf, 1, 7],[Inf, 4, 0, Inf, Inf],[2, Inf, -5, 0, Inf],[Inf, Inf, Inf, 6, 0]]Direction = [[0, 1, 2, 3, 4],[0, 1, 2, 3, 4],[0, 1, 2, 3, 4],[0, 1, 2, 3, 4],[0, 1, 2, 3, 4]] V = 5 #k就是是否要經過的節點,i就是開始節點,j就是終止節點 for k in range(V):for i in range(V):for j in range(V):if DIS[i][k] + DIS[k][j] < DIS[i][j]:DIS[i][j] = DIS[i][k] + DIS[k][j]Direction[i][j] = Direction[i][k]print('最終的距離矩陣{}'.format(np.array(DIS))) print('方向矩陣{}'.format(np.array(Direction)))def construct_path(Direction, i, j, path=[]):path = path + [Direction[i][j]]if Direction[i][j] == j:#終止條件return pathnew_path = construct_path(Direction, Direction[i][j], j, path)return new_path#找到路徑 for i in range(V):for j in range(V):path = construct_path(Direction, i, j, [i])print('{}--{}節點路徑為:{}'.format(i, j, path))?最終的距離矩陣和方向矩陣,其中方向矩陣可以用來還原開始節點到終止節點的路徑:
參考:
https://blog.csdn.net/weixin_43093481/article/details/82702176
https://www.bilibili.com/video/BV1q4411M7r9?from=search&seid=4042347737055062965
總結
以上是生活随笔為你收集整理的图论基础知识--最小生成树算法kruskal(克鲁斯克尔)和普里姆算法(Prim算法);最短路径算法Dijkstra(迪杰斯特拉)和Floyd(弗洛伊德)的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 天池入门赛--蒸汽预测
- 下一篇: 数据预测之BP神经网络具体应用以及mat