DFS查找依赖路径并按依赖层级展示生产的数据

背景

有如下场景:

// 定义结构体 dep
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找出全部的依赖路径,有间接依赖的也需要找出,并检查是否有循环依赖,依赖路径如下:

全路径依赖:
map[A:[[A F] [A B F] [A B C D E]] B:[[B F] [B C D E]] C:[[C D E B] [C D E B F]] D:[[D E B F] [D E B C]] E:[[E B F] [E B C D]]]
D:
  -> [D E B F]
  -> [D E B C]
E:
  -> [E B F]
  -> [E B C D]
A:
  -> [A F]
  -> [A B F]
  -> [A B C D E]
B:
  -> [B F]
  -> [B C D E]
C:
  -> [C D E B]
  -> [C D E B F]

根据依赖关系生成依赖数据,并记录层级关系,比如A:100 A->F的依赖系数是0.1,那么A->F这个路径计算得出F需要100*0.1=10;生成的数据如下:

// 根据依赖模型生成的依赖数据,按依赖关系生成依赖树
type DepData struct {
    ID          int     // 唯一id
    Src         string  //
    Depend      string  // 依赖
    RootID      int     // 根节点id
    ParentID    int     // 父节点id
    ResourceNum float64 // 依赖资源量
    Children    []*DepData
}

nodes := []*DepData{
        {ID: 1, Src: "A", RootID: 0, ParentID: 0, Depend: "B"},
        {ID: 2, Src: "B", RootID: 1, ParentID: 1, Depend: "C"},
        {ID: 3, Src: "C", RootID: 1, ParentID: 2, Depend: "D"},
        {ID: 4, Src: "B", RootID: 1, ParentID: 1, Depend: "F"},
        {ID: 5, Src: "A", RootID: 0, ParentID: 0, Depend: "F"},
        {ID: 6, Src: "F", RootID: 5, ParentID: 5, Depend: "G"},
        //...
}

需要将生成的数据按两种方式展示:
1. 根节点及其所有的子节点,两层展示

2. 根节点及其所有子节点逐层级展示,有多少层展示多少层

方案

完整实现代码如下:

package main

import (
    "fmt"
    "strings"
)

/*
如depModel所定义,src依赖Depend,可扩展依赖的具体内容,比如资源量的依赖关系,补充上依赖系数,即可根据源的资源量计算出所需依赖的资源量
依赖数据结构如下:
deps := []depModel{
        {"A", "B"},
        {"A", "F"},
        {"B", "C"},
        {"B", "F"},
        {"C", "D"},
        {"D", "E"},
        {"E", "B"}, // 这里是一个循环依赖
}
根据依赖关系生成的依赖数据结构如下:
nodes := []*DepData{
        {ID: 1, Src: "A", RootID: 0, ParentID: 0, Depend: "B"},
        {ID: 2, Src: "B", RootID: 1, ParentID: 1, Depend: "C"},
        {ID: 3, Src: "C", RootID: 1, ParentID: 2, Depend: "D"},
        {ID: 4, Src: "B", RootID: 1, ParentID: 1, Depend: "F"},
        {ID: 5, Src: "A", RootID: 0, ParentID: 0, Depend: "F"},
        {ID: 6, Src: "F", RootID: 5, ParentID: 5, Depend: "G"},
        //...
}
要生成依赖的数据,首先要从顶层找到每一个节点的依赖路径,并要检查是否有循环依赖
生成数据时需要记录根节点及父节点,然后支持按层级展示,按层级可选展示根和所有子节点两层,也可以从根节点逐层展示各层子节点
这里实现有:
1.根据依赖关系生成全部依赖路径的实现
2.检查依赖是否有环的实现
3.展示根和其所有子节点两层展示实现
4.展示根节点及逐层展示各层子节点实现
*/

// 定义结构体 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)
        }
    }
}

// 非递归深度优先搜索,查找全部依赖路径,并检查是否有循环依赖, 如果不同业务依赖模型不同,且要按业务优先找模型,则使用这里的方法,生成唯一key时加上业务名
//func nonRecursiveDFSGetProductDependencePath(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
//
//        defaultNode := ""
//        if !strings.HasPrefix(node, "default") {
//            nodeList := strings.Split(node, ",")
//            defaultNode = strings.Join(append([]string{"default"}, nodeList[1:]...), ",")
//        }
//
//        if neighbors, exists := graph[node]; exists {
//            // 组件依赖其他组件
//            for _, neighbor := range neighbors {
//                //if productDependencePathContains(path, neighbor) {
//                if _, has := visited[neighbor]; has {
//                    // 检测到循环依赖,直接返回
//                    results[startNode] = append(results[startNode], append(path, neighbor))
//                    // 如果循环节点不需要记录在path里,则这里path不用加循环节点neighbor,如下:
//                    // results[startNode] = append(results[startNode], path)
//                    circularDependency := strings.Join(path, " -> ") + " -> " + neighbor
//                    log.Info("circularDependency path: %s", circularDependency)
//                    // 如果要获取所有的依赖路径,这里continue就行了,不用return
//                    // continue
//                    return true, circularDependency
//                }
//
//                newPath := append([]string{}, path...)
//                newPath = append(newPath, neighbor)
//                newVisited := copyVisitedProductDependenceNode(visited)
//                newVisited[neighbor] = true
//                stack = append(stack, stackItem{neighbor, newPath, newVisited})
//            }
//        } else if neighbors1, defaultExists := graph[defaultNode]; defaultNode != "" && defaultExists {
//            // 组件依赖其他组件
//            for _, neighbor := range neighbors1 {
//                //if productDependencePathContains(path, neighbor) {
//                if _, has := visited[neighbor]; has {
//                    // 检测到循环依赖,直接返回
//                    results[startNode] = append(results[startNode], append(path, neighbor))
//                    // 如果循环节点不需要记录在path里,则这里path不用加循环节点neighbor,如下:
//                    // results[startNode] = append(results[startNode], path)
//                    circularDependency := strings.Join(path, " -> ") + " -> " + neighbor
//                    log.Info("circularDependency path: %s", circularDependency)
//                    // 如果要获取所有的依赖路径,这里continue就行了,不用return
//                    // continue
//                    return true, circularDependency
//                }
//
//                newPath := append([]string{}, path...)
//                newPath = append(newPath, neighbor)
//                newVisited := copyVisitedProductDependenceNode(visited)
//                newVisited[neighbor] = true
//                stack = append(stack, stackItem{neighbor, newPath, newVisited})
//            }
//        } else {
//            // 组件不依赖其他组件,记录依赖路径
//            results[startNode] = append(results[startNode], path)
//        }
//    }
//    return false, ""
//}

// 非递归深度优先搜索  检查是否有循环依赖
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 CheckCircleDep() {
    // 示例输入
    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)
        }
    }

}

// 根据依赖模型生成的依赖数据,按依赖关系生成依赖树
type DepData struct {
    ID          int     // 唯一id
    Src         string  //
    Depend      string  // 依赖
    RootID      int     // 根节点id
    ParentID    int     // 父节点id
    ResourceNum float64 // 依赖资源量
    Children    []*DepData
}

// DepDataEveryLevelTree 按依赖关系生成依赖树,每一层之间的关系都要显示
func DepDataEveryLevelTree() {
    nodes := []*DepData{
        {ID: 1, Src: "A", RootID: 0, ParentID: 0, Depend: "B"},
        {ID: 2, Src: "B", RootID: 1, ParentID: 1, Depend: "C"},
        {ID: 3, Src: "C", RootID: 1, ParentID: 2, Depend: "D"},
        {ID: 4, Src: "B", RootID: 1, ParentID: 1, Depend: "F"},
        {ID: 5, Src: "A", RootID: 0, ParentID: 0, Depend: "F"},
        {ID: 6, Src: "F", RootID: 5, ParentID: 5, Depend: "G"},
        //...
    }

    nodeMap := mapNodesByParent(nodes)
    rootNodes := buildTree(nodeMap, 0) // 假设所有 root 节点的 ParentID = 0
    for i := range rootNodes {
        fmt.Printf("data %d tree: %v\n", i, rootNodes[i])
    }
    printNodes(rootNodes, 0)
}

func mapNodesByParent(nodes []*DepData) map[int][]*DepData {
    nodeMap := make(map[int][]*DepData)
    for _, node := range nodes {
        nodeMap[node.ParentID] = append(nodeMap[node.ParentID], node)
    }
    return nodeMap
}

func buildTree(nodeMap map[int][]*DepData, parentID int) []*DepData {
    nodes := nodeMap[parentID]
    for _, node := range nodes {
        node.Children = buildTree(nodeMap, node.ID)
    }
    return nodes
}

func printNodes(nodes []*DepData, level int) {
    for i, node := range nodes {
        if level == 0 {
            fmt.Printf("data %d tree: %v\n", i, node)
        }
        tabs := strings.Repeat("\t", level)
        fmt.Printf("%s%d, %s, %s\n", tabs, node.ID, node.Src, node.Depend)
        printNodes(node.Children, level+1)
    }
}

// DepDataTwoLevelTree 按依赖关系生成依赖树,只显示根节点和所有子节点两层之间的关系
func DepDataTwoLevelTree() {
    nodes := []*DepData{
        {ID: 1, Src: "A", RootID: 0, ParentID: 0, Depend: "B"},
        {ID: 2, Src: "B", RootID: 1, ParentID: 1, Depend: "C"},
        {ID: 3, Src: "C", RootID: 1, ParentID: 2, Depend: "D"},
        {ID: 4, Src: "B", RootID: 1, ParentID: 1, Depend: "F"},
        {ID: 5, Src: "A", RootID: 0, ParentID: 0, Depend: "F"},
        {ID: 6, Src: "F", RootID: 5, ParentID: 5, Depend: "G"},
        //...
    }

    nodeMap := mapNodesByRoot(nodes)
    rootNodes := buildTreeByRoot(nodeMap, nodes)
    for i := range rootNodes {
        fmt.Printf("data %d tree: %v\n", i, rootNodes[i])
        for _, children := range rootNodes[i].Children {
            fmt.Printf("children: %v\n", children)
        }
    }
}

func buildTreeByRoot(nodeMap map[int][]*DepData, nodes []*DepData) []*DepData {
    rootNodes := make([]*DepData, 0)
    for _, node := range nodes {
        if node.RootID == 0 {
            rootNodes = append(rootNodes, node)
        }
        node.Children = nodeMap[node.ID]
    }
    return rootNodes
}

func mapNodesByRoot(nodes []*DepData) map[int][]*DepData {
    nodeMap := make(map[int][]*DepData)
    for _, node := range nodes {
        // 根节点不记录在map中
        if node.RootID == 0 {
            continue
        }
        nodeMap[node.RootID] = append(nodeMap[node.RootID], node)
    }
    return nodeMap
}

func main() {
    CheckCircleDep()
    DepDataEveryLevelTree()
    DepDataTwoLevelTree()
}

 

posted on 2024-08-21 14:24  生活费  阅读(11)  评论(0编辑  收藏  举报

导航