首页 > 其他分享 >DFS查找依赖路径

DFS查找依赖路径

时间:2024-08-09 12:05:22浏览次数:12  
标签:startNode string map 路径 results DFS 查找 path stack

背景:

有如下场景:

// 定义结构体 dep,表示 Src依赖Depend
type depModel struct { 
    Src    string `json:"src"`   // 源
    Depend string `json:"depend"`  // 依赖
}


// 示例输入
    deps := []depModel{
        {"A", "B"},
        {"A", "F"},
        {"B", "C"},
        {"B", "F"},
        {"C", "D"},
        {"D", "E"},
        {"E", "B"}, // 这里是一个循环依赖
    }

需要根据deps找出全部的依赖路径,有间接依赖的也需要找出,并检查是否有循环依赖。

方案

使用DFS来进行查找

实现逻辑如下:

package main

import (
    "fmt"
    "strings"
)

// 定义结构体 dep
type depModel struct {
    Src    string `json:"src"`
    Depend string `json:"depend"`
}

// 构建依赖图
func buildGraph(deps []depModel) map[string][]string {
    graph := make(map[string][]string)
    for _, d := range deps {
        graph[d.Src] = append(graph[d.Src], d.Depend)
    }
    return graph
}

// 非递归深度优先搜索
func nonRecursiveDFS(graph map[string][]string, startNode string, results map[string][][]string) {
    type stackItem struct {
        node    string          // 当前节点
        path    []string        // 记录当前节点依赖路径
        visited map[string]bool // 记录访问节点
    }

    stack := []stackItem{{startNode, []string{startNode}, map[string]bool{startNode: true}}}

    for len(stack) > 0 {
        item := stack[len(stack)-1]
        stack = stack[:len(stack)-1]
        node := item.node
        path := item.path
        visited := item.visited

        if neighbors, exists := graph[node]; exists {
            for _, neighbor := range neighbors {
                if _, has := visited[neighbor]; has {
                    // 检测到循环依赖
                    //results[startNode] = append(results[startNode], append(path, neighbor))
                    // 如果循环依赖的节点不需要在路径中,path不要添加当前节点即可,如下:
                    results[startNode] = append(results[startNode], path)
                    continue
                }

                newPath := append([]string{}, path...)
                newPath = append(newPath, neighbor)
                newVisited := copyVisited(visited)
                newVisited[neighbor] = true
                stack = append(stack, stackItem{neighbor, newPath, newVisited})
            }
        } else {
            results[startNode] = append(results[startNode], path)
        }
    }
}

// 非递归深度优先搜索
func nonRecursiveDFSCheckCircle(graph map[string][]string, startNode string, results map[string][][]string) (bool, string) {
    type stackItem struct {
        node    string          // 当前节点
        path    []string        // 记录当前节点依赖路径
        visited map[string]bool // 记录访问节点
    }

    stack := []stackItem{{startNode, []string{startNode}, map[string]bool{startNode: true}}}

    for len(stack) > 0 {
        item := stack[len(stack)-1]
        stack = stack[:len(stack)-1]
        node := item.node
        path := item.path
        visited := item.visited

        if neighbors, exists := graph[node]; exists {
            for _, neighbor := range neighbors {
                if _, has := visited[neighbor]; has {
                    // 检测到循环依赖
                    results[startNode] = append(results[startNode], append(path, neighbor))
                    circularDependency := strings.Join(path, " -> ") + " -> " + neighbor
                    // 如果循环依赖的节点不需要在路径中,path不要添加当前节点即可,如下:
                    //results[startNode] = append(results[startNode], path)
                    return true, circularDependency
                }

                newPath := append([]string{}, path...)
                newPath = append(newPath, neighbor)
                newVisited := copyVisited(visited)
                newVisited[neighbor] = true
                stack = append(stack, stackItem{neighbor, newPath, newVisited})
            }
        } else {
            results[startNode] = append(results[startNode], path)
        }
    }
    return false, ""
}

// 检查路径中是否包含节点
func pathContains(path []string, node string) bool {
    for _, n := range path {
        if n == node {
            return true
        }
    }
    return false
}

// 复制访问节点记录
func copyVisited(visited map[string]bool) map[string]bool {
    newVisited := make(map[string]bool)
    for k, v := range visited {
        newVisited[k] = v
    }
    return newVisited
}

func findDependencies(deps []depModel) map[string][][]string {
    graph := buildGraph(deps)
    Results := make(map[string][][]string)

    for node := range graph {
        nonRecursiveDFS(graph, node, Results)
    }

    return Results
}

func HasCircleDep(deps []depModel) (bool, string) {
    graph := buildGraph(deps)
    Results := make(map[string][][]string)

    for node := range graph {
        hasCircle, circlePath := nonRecursiveDFSCheckCircle(graph, node, Results)
        if hasCircle {
            return hasCircle, circlePath
        }
    }
    return false, ""
}

func main() {
    // 示例输入
    deps := []depModel{
        {"A", "B"},
        {"A", "F"},
        {"B", "C"},
        {"B", "F"},
        {"C", "D"},
        {"D", "E"},
        {"E", "B"}, // 这里是一个循环依赖
    }

    // 检查是否有循环依赖
    hasCircle, circlePath := HasCircleDep(deps)
    if hasCircle {
        fmt.Println("存在循环依赖:", circlePath)
    } else {
        fmt.Println("没有循环依赖")
    }

    // 调用 findDependencies 函数,获取全路径依赖
    results := findDependencies(deps)

    // 输出结果
    fmt.Println("全路径依赖:")
    fmt.Println(results)
    for src, paths := range results {
        fmt.Printf("%s:\n", src)
        for _, path := range paths {
            fmt.Printf("  -> %v\n", path)
        }
    }

}

 

标签:startNode,string,map,路径,results,DFS,查找,path,stack
From: https://www.cnblogs.com/276815076/p/18350548

相关文章

  • SVG之Path路径详解(二),全面解析贝塞尔曲线
    前言如果没看过上一篇文章,可以点击链接前往观看,循序渐进,体验更佳在进入正题前,先温习一下svg的坐标系,x轴为水平向右,y轴为垂直向下在前一篇文章中,我们已经了解了d属性的M、L、H、V、A命令,接下来,将继续了解剩下命令d属性详解主要定义了路径的路径数据,由描述路径的一系列命令数......
  • 最大匹配、最小顶点覆盖、最大独立集、最小路径覆盖(转)(再转)
    在讲述这两个算法之前,首先有几个概念需要明白:二分图:二分图又称二部图,是图论中的一种特殊模型。设G=(V,E)是一个无向图,如果顶点V可以分割为两个互不相交的子集(A,B),并且图中的每条边(i,j)所关联的两个顶点i和j分别属于这两个不同的顶点集(iinA,jinB),则称图G是二分图。......
  • 统计HDFS中文件数量、大小、以及在某范围大小的文件数量
    说明:统计HDFS文件数量大小,小于20M文件数量1、HDFS相关命令#统计文件大小hdfsdfs-du-h/#统计文件数量,返回的数据是目录个数,文件个数,文件总计大小,输入路径hdfsdfs-count/#统计所有文件的信息,过滤文件夹,只统计文件,因为使用-ls-R之后,可以看到文件是”-“......
  • 清晰易懂二分查找算法 你确定不看吗?
    @目录前言简介一、二分查找算法的原理是什么?1.确定搜索范围:2.计算中间位置:3.比较中间元素:4.调整搜索范围:5.重复迭代:二、二分查找算法的优缺点是什么?优点:缺点:三、java实现二分查找案例总结前言请各大网友尊重本人原创知识分享,谨记本人博客:南国以南i、提示:以下是本篇文......
  • 为什么并查集路径压缩不需要维护rank?
    在基于rank进行优化的并查集中,路径压缩确实不需要维护rank数组。这是因为路径压缩和rank优化有不同的目的和作用机制。让我们详细解释一下原因:Rank优化的目的:Rank优化的主要目的是在合并两个集合时,让较小的树成为较大的树的子树,以保持树的平衡性。这样可以避免树变得过于深,从而......
  • 不能在此路径中使用此配置节,如果在父级别上锁定了该节,便会出现这种情况的解决办法
    原文链接:https://www.pageadmin.net/help/96.cshtml问题描述:打开网站一直提示“不能在此路径中使用此配置节。如果在父级别上锁定了该节,便会出现这种情况。锁定是默认设置的(overrideModeDefault="Deny")”具体如下图:问题分析:出现这个错误是因为IIS7采用了更安全的web.con......
  • 路径规划算法之Dijistra算法、A*算法
    引言记录一下这段时间了解到的路径规划算法,并进行代码的实现目前主流的路径规划算法可以分为:基于采样,如RRT、RRT*、PRM基于节点,如Dijistra、A、D基于数学模型,如MILP、NLP基于生物启发式,如NN、GN多融合,如PRM-NodebasedDijistraalgorithm问题描述Dijistra算法主要......
  • 不能在此路径中使用此配置节。如果在父级别上锁定了该节,便会出现这种情况。锁定是默认
    原文链接:https://www.cnblogs.com/wwssgg/p/17984105今天运行项目的时候出现了这个错误....查了一下解决的方法。 具体方案如下: 1、先确认安装IIS的时候有没有装Asp.Net,如果没安装的话,安装上即可。(XTHS:采用这步,就可以了!) 2、IIS采用了更安全的web.config管理机制,默......
  • 数据结构 - 并查集路径压缩
    ......
  • AOE网及其求解关键路径
    全称ActivityonEdgeNetwork边活动网特点仅存在有向无环图 作用用于记录完成整个工程至少花费的时间==>哪条路径最耗时?也就是“关键路径”AOE网元素介绍关键活动关键路径上的活动称为关键活动,关键活动是不允许拖延的(普通活动可以拖延,拖延时间=最晚开始时......