LeetCode算法笔记

文章目录

前言

一、拓扑排序

二、拓扑排序的实现方法

1.Kahn 算法

2.基于 DFS 实现拓扑排序算法

总结



前言

Datawhale十月组队学习,LeetCode算法

一、拓扑排序

拓扑排序(Topological Sorting):一种对有向无环图(DAG)的所有顶点进行线性排序的方法,使得图中任意一点 u 和 v,如果存在有向边 <u, v>,则 u必须在 v 之前出现。对有向图进行拓扑排序产生的线性序列称为满足拓扑次序的序列,简称拓扑排序。

图的拓扑排序是针对有向无环图(DAG)来说的,无向图和有向有环图没有拓扑排序,或者说不存在拓扑排序。

二、拓扑排序的实现方法

1.Kahn 算法

代码如下:

import collections

class Solution:
    # 拓扑排序,graph 中包含所有顶点的有向边关系(包括无边顶点)
    def topologicalSortingKahn(self, graph: dict):
        indegrees = {u: 0 for u in graph}   # indegrees 用于记录所有顶点入度
        for u in graph:
            for v in graph[u]:
                indegrees[v] += 1           # 统计所有顶点入度
        
        # 将入度为 0 的顶点存入集合 S 中
        S = collections.deque([u for u in indegrees if indegrees[u] == 0])
        order = []                          # order 用于存储拓扑序列
        
        while S:
            u = S.pop()                     # 从集合中选择一个没有前驱的顶点 0
            order.append(u)                 # 将其输出到拓扑序列 order 中
            for v in graph[u]:              # 遍历顶点 u 的邻接顶点 v
                indegrees[v] -= 1           # 删除从顶点 u 出发的有向边
                if indegrees[v] == 0:       # 如果删除该边后顶点 v 的入度变为 0
                    S.append(v)             # 将其放入集合 S 中
        
        if len(indegrees) != len(order):    # 还有顶点未遍历(存在环),无法构成拓扑序列
            return []
        return order                        # 返回拓扑序列
    
    
    def findOrder(self, n: int, edges):
        # 构建图
        graph = dict()
        for i in range(n):
            graph[i] = []
            
        for u, v in edges:
            graph[u].append(v)
            
        return self.topologicalSortingKahn(graph)

 这个算法类似于广度优先搜索BFS,利用循环查出数据在graph中的出现次数,在利用循环从集合S中入度为零的节点开始访问,将访问过的数据存入队列中,循环过的记录出现过数据的值减一,若值为零,则存入集合S中,直到集合S为空。若这组数据为拓补排序,则数据的访问与基于队列实现的广度优先搜索算法类似,都是访问当前节点指向的所有节点,再访问记录的下一个节点,但这个有所不同,在访问下一个节点时,这个算法是按照graph这组数据的顺序来访问的,比广度优先搜索好理解。这个算法判断是否有环,是判断数据是否访问完全。

2.基于 DFS 实现拓扑排序算法

代码如下:

import collections

class Solution:
    # 拓扑排序,graph 中包含所有顶点的有向边关系(包括无边顶点)
    def topologicalSortingDFS(self, graph: dict):
        visited = set()                     # 记录当前顶点是否被访问过
        onStack = set()                     # 记录同一次深搜时,当前顶点是否被访问过
        order = []                          # 用于存储拓扑序列
        hasCycle = False                    # 用于判断是否存在环
        
        def dfs(u):
            nonlocal hasCycle
            if u in onStack:                # 同一次深度优先搜索时,当前顶点被访问过,说明存在环
                hasCycle = True
            if u in visited or hasCycle:    # 当前节点被访问或者有环时直接返回
                return
            
            visited.add(u)                  # 标记节点被访问
            onStack.add(u)                  # 标记本次深搜时,当前顶点被访问
    
            for v in graph[u]:              # 遍历顶点 u 的邻接顶点 v
                dfs(v)                      # 递归访问节点 v
                    
            order.append(u)                 # 后序遍历顺序访问节点 u
            onStack.remove(u)               # 取消本次深搜时的 顶点访问标记
        
        for u in graph:
            if u not in visited:
                dfs(u)                      # 递归遍历未访问节点 u
        
        if hasCycle:                        # 判断是否存在环
            return []                       # 存在环,无法构成拓扑序列
        order.reverse()                     # 将后序遍历转为拓扑排序顺序
        return order                        # 返回拓扑序列
    
    def findOrder(self, n: int, edges):
        # 构建图
        graph = dict()
        for i in range(n):
            graph[i] = []
        for v, u in edges:
            graph[u].append(v)
        
        return self.topologicalSortingDFS(graph)

基于深度优先搜素DFS算法实现,在学习过深度优先搜素基于递归实现之后,在代码中最重要以及难点是这一组数据是否是有向无环的,即判断在深度搜索一支数据串时 ,没有形成环;次要难点是深度优先搜素的递归调用的过程中,若在某一节点的下一个节点都被访问过,则要访问这一节点的上一节点。我认为是若这一节点的下一节点都被访问过,则结束这一次递归调用,直接执行上一次递归调用的剩余循环。


总结

我认为拓扑排序是一组有向无环的线性结构。

在kahn算法中利用类似广度优先搜索的方法,记录数据存在的次数,看最后队列中存在的数据是否访问完的,来判断是否存在环。

在DFS算法中利用深度优先搜素基于递归实现来访问数据,在同一递归中判断该节点是否被访问过,来判断是否有环。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

霜枫91

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值