数据结构与算法Python版 拓扑排序与强连通分支


一、图的应用-拓扑排序

拓扑排序Topological Sort

  • 工作流程图得到工作次序排列的算法,称为“拓扑排序”
  • 拓扑排序处理一个有向无环图DAG,输出顶点的线性序列。使得两个顶点v,w,如果图中有(v,w)边,在线性
    序列中v就出现在w之前。
  • 拓扑排序广泛应用在依赖事件的排期上,还可以用在项目管理、数据库查询优化和矩阵乘法的次序优化上

拓扑排序的实现

  • 拓扑排序可以采用深度优先搜索DFS实现。
    • 将工作流程建立为图,工作项是节点依赖关系是有向边。工作流程图一定是个有向无环图DAG,否则出现循环依赖。
    • 对DAG图调用DFS算法,以得到每个顶点的“结束时间”,按照每个顶点的“结束时间”从大到小排序,输出这个次序下的顶点列表
  • 根据遍历次序的不同,拓扑排序的结果可能有多个

示例:

在这里插入图片描述

# 将工作流程建立为图
dfs_g = DFSGraph()
dfs_g.add_edge("3/4 cup milk", "1 cup mix")
dfs_g.add_edge("1 egg", "1 cup mix")
dfs_g.add_edge("1 tbl oil", "1 cup mix")
dfs_g.add_edge("1 cup mix", "pour 1/4 cup")
dfs_g.add_edge("1 cup mix", "heat syrup")
dfs_g.add_edge("heat griddle", "pour 1/4 cup")
dfs_g.add_edge("pour 1/4 cup", "turn when bubbly")
dfs_g.add_edge("turn when bubbly", "eat")
dfs_g.add_edge("heat syrup", "eat")

dfs_g.dfs()
res = []
for i in dfs_g.vertexes.values():
    res.append((i.finish, i.key))
# 按照每个顶点的“结束时间”从大到小排序
res.sort(key=lambda x: x[0], reverse=True)
print("拓扑排序结果:", res)


### 输出结果
拓扑排序结果: [(18, 'heat griddle'), (16, '1 tbl oil'), (14, '1 egg'), (12, '3/4 cup milk'), (11, '1 cup mix'), (10, 'heat syrup'), (8, 'pour 1/4 cup'), (7, 'turn when bubbly'), (6, 'eat')]

在这里插入图片描述

二、图的应用-强连通分支

强连通分支Strongly Connected Components

  • 强连通分支,定义为图G的一个子集C。C中的任意两个顶点v,w之间都有路径来回,即(v,w)(w,v)都是C的路径,而且C是具有这样性质的最大子集
  • Transposition转置:一个有向图G的转置GT,定义为将图G的所有边的顶点交换次序,如将(v,w)转换为(w,v)。图和转置图在强连通分支的数量和划分上,是相同的。
  • 常用强连通分支算法有Tarjan算法、Gabow算法(对Tarjan的改进)和Kosaraju算法,这些算法的基本思想是进行深度优先搜索,并通过标记来识别强连通分支。

在这里插入图片描述

寻找强连通分支(在图中发现高度聚集节点群)的应用

  • 网络分析
    • 在社交网络分析中,强连通分支可以帮助识别紧密联系的小团体或者社区。
    • 在通信网络中,强连通分支可用于优化路由策略,提高网络的稳定性和效率。
  • 算法设计:在算法设计中,特别是在有向图的处理算法中,例如拓扑排序,识别强连通分支可以简化问题,使得算法更加高效。
  • 系统建模:在系统动态和反馈系统分析中,强连通分支能够揭示系统内部各部分之间的相互作用和依赖关系。
  • 程序分析:在软件工程中,特别是在编译器的控制流分析中,强连通分支有助于理解程序中的循环结构,优化代码。
  • 经济学和博弈论:在经济学模型中,强连通分支可以用来分析市场中的不同参与者之间的相互作用和影响。
  • 生物信息学:在生物信息学中,可以通过分析蛋白质之间的相互作用网络,利用强连通分支来识别关键的生物分子和途径。
  • 数据挖掘:在数据挖掘中,识别大型数据库中的强连通分支有助于发现数据之间的深层次联系。

在这里插入图片描述

强连通分支算法-Kosaraju算法思路

  • 首先,对图G调用深度优先搜索DFS算法,为每个顶点计算“结束时间”;
  • 然后,将图G进行转置,得到GT;
  • 再对GT调用DFS算法,但在dfs函数中,对每个顶点的搜索循环里,要以顶点的“结束时间”倒序的顺序来搜索。
  • 每次DFS访问到的节点构成一个强连通分支

Kosaraju算法实现

  • DFSGraph类:
    • 更新dfs方法的形参,可以指定顶点的遍历次序;
    • 更新dfsvisit方法的形参,增加形参tmp记录顶点递归次序
### 更新后的DFSGraph类
class DFSGraph(Graph):
    def __init__(self):
        super().__init__()
        self.time = 0

    def dfs(self, keys_order=None):
        """keys_order参数:可指定遍历顶点的次序"""
        # 初始化顶点颜色和前驱
        for v in self:
            v.set_color("white")
            v.set_pred(-1)
        # 遍历未访问的顶点
        if keys_order:
            for key in keys_order:
                v = self.vertexes.get(key)
                if v.get_color() == "white":
                    scc = [] # 记录每个强连通分支
                    self.dfsvisit(v, scc)
                    print(scc)
        else:
            for v in self:
                if v.get_color() == "white":
                    self.dfsvisit(v)

    def dfsvisit(self, start_v: Vertex, tmp=[]):
        tmp.append(start_v.key)
        start_v.set_color("gray")
        self.time += 1
        # 记录第几步访问到这个顶点
        start_v.discovery = self.time
        for next_v in start_v.get_connections():
            # 递归访问未访问的顶点并记录前驱
            if next_v.get_color() == "white":
                next_v.set_pred(start_v)
                self.dfsvisit(next_v, tmp)
        start_v.set_color("black")
        self.time += 1
        # 记录在第几步完成了此顶点探索
        start_v.finish = self.time
        
        

强连通分支-示例

edges = [
    ["A", "B"],
    ["B", "C"],
    ["B", "E"],
    ["C", "C"],
    ["C", "F"],
    ["D", "B"],
    ["D", "G"],
    ["E", "D"],
    ["E", "A"],
    ["F", "H"],
    ["G", "E"],
    ["H", "I"],
    ["I", "F"],
]
# 对图G调用DFS算法,为每个顶点计算“结束时间”
dfs_g = DFSGraph()
for i in edges:
    dfs_g.add_edge(i[0], i[1])
dfs_g.dfs()
res = []
for i in dfs_g.vertexes.values():
    res.append((i.finish, i.key))
res.sort(key=lambda x: x[0], reverse=True)  # “结束时间”从大到小排序
print("第一趟DFS按“结束时间”从大到小排序:")
print(res)
keys_order = [i[1] for i in res]


### 输出结果
第一趟DFS按“结束时间”从大到小排序:
[(18, 'A'), (17, 'B'), (16, 'E'), (15, 'D'), (14, 'G'), (10, 'C'), (9, 'F'), (8, 'H'), (7, 'I')]

第一趟DFS

在这里插入图片描述

转置后第二趟DFS

# 按照第一步记录的完成时间逆序对反转图进行DFS
dfs_gt = DFSGraph()
for i in edges:
    dfs_gt.add_edge(i[1], i[0])
print("强连通分支如下:")
dfs_gt.dfs(keys_order)

在这里插入图片描述

强连通分支结果

### 输出结果
强连通分支如下:
['A', 'E', 'B', 'D', 'G']
['C']
['F', 'I', 'H']

在这里插入图片描述


您正在阅读的是《数据结构与算法Python版》专栏!关注不迷路~

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值