Pure Soul

  1. 首页
  2. Golang
  3. 正文

温故而知新 - 图

2022年1月1日 750点热度 0人点赞 0条评论

目录

  • 图
    • 图的复杂度
    • 稀疏图和稠密图及其储存方式
    • 拓扑排序
    • 最短路径算法
      • 无权边的最短路径算法
      • 带权图最短路径算法-Dijkstra

图


简单来说,图是由一组点和相对应边组成的,可以表示为G=<V,E>,V为Vertex(点集),E为Edge(边集)。细分下来根据点集之间的对等关系又可以分为,有向图(又可分为带权和无权有向图),无向图,混合图。图中点包含的另外一个的重要信息就是各自的出度(outdeg)和入度(indeg),表示了以该点作为出边和入边的个数。

图的复杂度


对于无向图而言,所有点的度之和为sum(deg)=2*|E|,度之和为边的数目的两倍。对于有向图而言,所有点的入度之和自然等于出度之和,也会等于边的数目,毕竟每一条边存在源点和终点。

稀疏图和稠密图及其储存方式


没有明确的界定公式表明什么是稠密图什么是稀疏图,一般而言如果边的数目远小于完全图的边数目的一半,那么认为是稀疏图。远大于则认为是稠密图。稀疏图适合使用使用邻接表储存,稠密图适合使用邻接矩阵储存。

拓扑排序


拓扑排序是对有向无环图的顶点的一种排序,可以使得如果存在一条从vi到vj的路径,那么vj一定排在vi后面。拓扑排序常见于工程的调度排班问题,子任务之间存在先后依赖关系。对有向图的拓扑排序算法伪代码如下

Graph=[...] # 初始化图的储存,使用邻接矩阵或者邻接表;
indegs=[deg0,deg1,deg2....] # 储存所有的点的入读
stack=[node0, node1...] # 使用栈stack储存所有入度为0的点;
ans=[] # 储存拓扑排序结果
while stack:
    topNode=stack.pop(0) # 取出第一个出度为0的点,从图中直接抹去
    ans.append(topNode)
    for u in Graph[topNode]: # 遍历取出的点所连接的点
        indegs[u]-=1 # 对应连接到的点入度减1
        if indegs[u]==0:
            stack.append(u) # 判断入度是否为0

实际代码为:

from collections import defaultdict

# 构建邻接表的有向图
N=7
graph={0:[1,5],
       1:[4],
       2:[4,6],
       3:[2,4],
       4:[5,6],
       5:[],
       6:[]}
# 计算每个点的入读
indegs=defaultdict(int)
for k,v in graph.items():
    for node in v:
        indegs[node]+=1
# 记录所有入度为0的起始点
stack=[]
for i in range(N):
    if i not in indegs:
        stack.append(i)
# 惊喜拓扑排序
ans=[] # 排序结果
while stack:
    topNode=stack.pop(0)
    ans.append(topNode)
    for v in graph[topNode]:
        indegs[v]-=1
        if indegs[v]==0:
            stack.append(v)
# 输出结果
print(ans)

最短路径算法


最短路径算法解决的是从给定的点开始求解到其他点的最短路径(边可以带权或者不带权值)。

无权边的最短路径算法


这种情况下可以认为边的权值为1,如果权值为1,那么这个问题可以简化为BFS搜索问题(或者DFS递归搜索问题),也就是寻找达到各个节点的最短路径。

Graph=[...] # 初始化图的储存,使用邻接矩阵或者邻接表;
stack=[(initNode,0)] # 初始节点入栈,一并记录最短路径
dist={} # 记录源点到达各个点的最短距离,除开源点之外其他的初始值设置为inf
visited=set() # 记录已经访问过的点
while stack:
    cur=stack.pop(0) # 取出一个节点
    for v in graph[cur]:
        # 遍历下一层节点

实际代码为:

graph={0:[1,5],
       1:[4],
       2:[4],
       3:[2,4],
       4:[5,6],
       5:[],
       6:[2]}
# 假设4 2 6成环,利用BFS进行查找
N=7 # 节点数
minDist={i:float('inf') for i in range(N)}
initNode=0
minDist[initNode]=0
stack=[(initNode, 0)]
visited=set(stack)
while stack:
    cur=stack.pop(0)
    visited.add(cur[0])
    for v in graph[cur[0]]:
        minDist[v]=min(minDist[v], cur[1]+1)
        if v in visited: continue
        stack.append((v, minDist[v]))
print(minDist)

带权图最短路径算法-Dijkstra


Dijstra算法是典型的BFS算法,同样的从源点开始搜索,直到达到最后的终点,基本的步骤如下:

  • 将所有的点划分为两个集合{S,U},其中S代表已经求出来的最短路径的顶点及其最短的路径长度,U表示还未求出的点的长度。最开始S中只包含一个起点,U中包含了其他所有的点。
  • 从U中选出距离最短的k,并将k加入S中,并从U中移除k
  • 更新U中节点到起点的距离,这一步操作是因为确定了k是最短路径的顶点,进而更新一些经过k的点的路径,例如存在<s, v> > <s,k>+<k, v>那么更新v点的最短路径
  • 重复操作前面两步
from typing import List, Dict

graph = {'A': [['B', 12],
               ['G', 14],
               ['F', 16]],
         'B': [['F', 7],
               ['C', 10],
               ['A', 12]],
         'G': [['F', 9],
               ['E', 8],
               ['A', 14]],
         'F': [['C', 6],
               ['E', 2],
               ['B', 7],
               ['G', 9],
               ['A', 16]],
         'C': [['E', 5],
               ['D', 3],
               ['B', 10],
               ['F', 6]],
         'E': [['C', 5],
               ['D', 4],
               ['F', 2],
               ['G', 8]],
         'D': [['C', 3],
               ['E', 4]]}

def Dijstra(graph: Dict[str, List[List]], start: str) -> List[List]:
    graphConvert = dict()
    for k, v in graph.items():
        for nodeName, dis in v:
            graphConvert[k + nodeName] = dis

    if start not in graph:
        return []  # 源点不存在图中
    U = []
    S = [[start, 0]]
    for k, _ in graph.items():
        if k == start: continue
        U.append([k, float('inf')])
    for node, distance in graph[start]:
        U.remove([node, float('inf')])
        U.append([node, distance])
    # 初始化S和U完毕

    while U:
        minNode, minDistance = '', float('inf')
        for nd, dis in U:
            if dis < minDistance:  # 在U中选取最小距离的节点
                minNode = nd
                minDistance = dis
        print(minNode, minDistance)
        # 更新U中的节点距离信息,放置<s, v> > <s, v>+<v, k>出现
        for i in range(len(U)):
            if minNode + U[i][0] not in graphConvert: continue
            if U[i][1] > minDistance + graphConvert[minNode + U[i][0]]:
                U[i][1] = graphConvert[minNode + U[i][0]] + minDistance  # 更新信息

        # 将节点加入S, 从U中移除节点
        S.append([minNode, minDistance])
        U.remove([minNode, minDistance])
    return S

ans = Dijstra(graph, 'D')
标签: 暂无
最后更新:2022年1月2日

ycq

这个人很懒,什么都没留下

点赞
< 上一篇
下一篇 >

COPYRIGHT © 2021 oo2ee.com. ALL RIGHTS RESERVED.

THEME KRATOS MADE BY VTROIS