153.拓扑排序典型应用场景:校验DAG中是否有环

在做一些较为底层的开发工作时,基本都会使用到流程编排,即将相关业务作为节点,组成有效无环图(DAG)。
在这里插入图片描述

拓扑排序是对有向无环图(DAG)的顶点进行排序,使得对于图中的每条有向边 (u, v),顶点u在排序结果中都出现在顶点v之前。同时,通过拓扑排序可以校验DAG中是否存在环。

以下是一个使用Go语言实现的拓扑排序算法来判断带有父节点、子节点的有向无环图(DAG)中是否有环的示例代码:

package main

import (
    "fmt"
)

// Node 结构体表示图中的节点
type Node struct {
    ID       string
    // 有时候还会定义一个bool类型的Root字段,标记当前节点是否是根节点
    Root     bool
    Children []*Node
    Parents  []*Node
}

// Graph 结构体表示整个图
type Graph struct {
    Nodes map[string]*Node
}

// NewGraph 创建一个新的图实例
func NewGraph() *Graph {
    return &Graph{
        Nodes: make(map[string]*Node),
    }
}

// AddNode 向图中添加一个节点
func (g *Graph) AddNode(id string) {
    if _, ok := g.Nodes[id];!ok {
        g.Nodes[id] = &Node{
            ID:       id,
            Children: []*Node{},
            Parents:  []*Node{},
        }
    }
}

// AddEdge 添加一条有向边,连接from节点到to节点
func (g *Graph) AddEdge(from, to string) {
    fromNode, ok := g.Nodes[from]
    if!ok {
        fmt.Printf("节点 %s 不存在于图中\n", from)
        return
    }
    toNode, ok := g.Nodes[to]
    if!ok {
        fmt.Printf("节点 %s 不存在于图中\n", to)
        return
    }
    fromNode.Children = append(fromNode.Children, toNode)
    toNode.Parents = append(toNode.Parents, fromNode)
}

// TopologicalSort 执行拓扑排序并判断是否有环,返回排序后的节点列表和是否有环的标志
func (g *Graph) TopologicalSort() ([]*Node, bool) {
    inDegree := make(map[*Node]int)
    for _, node := range g.Nodes {
        inDegree[node] = len(node.Parents)
    }

    var queue []*Node
    // 将入度为0的节点加入队列
    for node, degree := range inDegree {
        if degree == 0 {
            queue = append(queue, node)
        }
    }

    var sortedNodes []*Node
    hasCycle := false
    // 进行拓扑排序并判断是否有环
    // 类似树的层序遍历
    for len(queue) > 0 {
        node := queue[0]
        queue = queue[1:]
        sortedNodes = append(sortedNodes, node)

        // 更新相邻节点的入度,并将入度变为0的节点加入队列
        for _, child := range node.Children {
            inDegree[child]--
            if inDegree[child] == 0 {
                queue = append(queue, child)
            }
        }
    }

    // 如果排序后的节点数量不等于图中的节点数量,说明图中有环
    if len(sortedNodes)!= len(g.Nodes) {
        hasCycle = true
    }

    return sortedNodes, hasCycle
}

在上述代码中:

  • Node 结构体用于表示图中的一个节点,包含节点的唯一标识 ID,指向子节点的指针数组 Children 以及指向父节点的指针数组 Parents
  • Graph 结构体用于表示整个图,通过一个字典 Nodes 来存储图中的所有节点。
  • AddNode 方法用于向图中添加一个新的节点。
  • AddEdge 方法用于在图中添加一条有向边,连接指定的两个节点,并更新相关节点的 ChildrenParents 数组。
  • TopologicalSort 方法首先计算每个节点的入度(即指向该节点的父节点数量),然后将入度为 0 的节点放入队列。接着通过不断从队列中取出节点,更新相邻节点的入度,并将入度变为 0 的节点再次加入队列,同时判断是否能将所有节点都排序完成。如果排序后的节点数量不等于图中的节点数量,就说明图中有环,否则说明图中不存在环。最后返回排序后的节点列表以及是否有环的判断结果。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值