一.基础知识
有向图 无向图
以无向图为例:
邻接矩阵:
度矩阵(对角矩阵):
二.最小生成树
应用:将网络顶点看着城市,边看着城市之间通讯网,边的权重看着成本,根据最小生成树可以构建城市之间成本最低的通讯网.
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
dist[M] = 0 vst[M] = 1
dist[W] = 5 vst[W] = 0
dist[E] = 8 vst[E] = 0
dist[D] = inf vst[D] = 0
dist[X] = 10 vst[X] = 0
依次遍历每个顶点........
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