拓扑排序-有向图-环

拓扑排序主要用来解决有向图中的依赖解析(dependency resolution)问题
拓扑排序可以用来创建任务列表
用来检测有向图是不是有环

拓扑排序用到.png

上图如果要做B必须先A
从排序的角度就是A-B的顺序
网络路由是不是就这样啊

排序的有向图我们还是用https://www.jianshu.com/p/4d02b69e54e0这和里面的

有向图BFS用.png

从拓扑排序的原理来说顺序可以使0-1-2-3-4、0-1-3-2-4等等
首先放入的就是没有前向节点的0
0放入然后斩断与1、2、3的连接之后 这样1,2,3都可以插入了因为他们也没前向节点了1、2、3顺序无关
4必须在1、2、3之后

如何去做斩断操作呢?我们用一个数组记录所有节点的前项节点的个数比如
0-0
1-1
2-1
3-1
4-3
前面是key值后面是是个数
下面的代码和其他的文章有重复、主要为了避免去重复看另一个文章
队列的代码


// 队列
/*
队列的特性较为单一,基本操作即初始化、获取大小、添加元素、移除元素等。
最重要的特性就是满足先进先出
*/
type linkNode struct {
    value MapParent
    next  *linkNode
}

type linkedList struct {
    head  *linkNode
    tail  *linkNode
    count int
}

func NewLinkList() *linkedList {
    return &linkedList{head: nil, tail: nil, count: 0}
}

func (this *linkedList) IsEmpty() bool {
    return this.count == 0
}

func (this *linkedList) Add(value MapParent) {
    node := new(linkNode)
    node.value = value

    this.count++
    if this.tail == nil {
        this.head = node
        this.tail = node
        node.next = nil
        return
    }

    this.tail.next = node
    node.next = nil
    this.tail = node
}

func (this *linkedList) Delete() *linkNode {
    if this.head == nil {
        return nil
    }

    this.count--
    if this.head == this.tail {
        node := this.head
        this.head = nil
        this.tail = nil

        return node
    }

    node := this.head
    this.head = this.head.next

    return node
}

type Queue struct {
    link *linkedList
}

func NewQueue() *Queue {
    return &Queue{link: NewLinkList()}
}

//加入队列
func (this *Queue) Put(value MapParent) {
    this.link.Add(value)
}

//pop出队列
func (this *Queue) Pop() *linkNode {
    return this.link.Delete()
}

//获得队列的长度
func (this *Queue) GetSize() int {
    return this.link.count
}

func (this *Queue) IsEmpty() bool {
    return this.GetSize() == 0
}


图的代码

package main

import (
    "fmt"
    "strconv"
)

// 逻辑不是很严谨  越界的没考虑-- scanf

// 边节点结构
type EdgeTableNode struct {
    index         int            // 顶点索引
    weight        int            // 权重
    edgeTableNode *EdgeTableNode // 下一个临界点的指针
}

// 顶点的数据信息
type VertInfo struct {
    value int
    name  string
}

// 顶点节点
type VertNode struct {
    vertInfo      VertInfo //  定点的数据信息
    edgeTableNode *EdgeTableNode
}

type Graph struct {
    vertNum  int
    grapType uint8
    edgeNum  int

    hasCircle bool
    allCircle [][]int
    visted    []int

    vertNode []*VertNode
}

var arrayList []int

func NewGraph(vertNum int, edgeNum int, grapType uint8) *Graph {
    return &Graph{vertNum: vertNum, hasCircle: false, allCircle: [][]int{},
        visted: make([]int, vertNum), grapType: grapType,
        edgeNum: edgeNum, vertNode: []*VertNode{}}
}

// 只做了有向图的初始化
// 没考虑无向图
func (this *Graph) InitGrap() {
    // 顶点初始化
    for i := 0; i < this.vertNum; i++ {
        vert := &VertNode{}

        vert.vertInfo.value = i
        vert.vertInfo.name = "V" + strconv.Itoa(i)

        fmt.Println(*vert)

        this.vertNode = append(this.vertNode, vert)
    }

    // 边初始化
    var startVert int
    var endVert int
    var weight int
    var n int
    for i := 0; i < this.edgeNum; i++ {
        n, _ = fmt.Scanf("%d %d %d", &startVert, &endVert, &weight)

        fmt.Printf("%d %d %d\n", startVert, endVert, n)

        var edgeNode = this.vertNode[startVert].edgeTableNode
        if edgeNode == nil {
            tempNode := new(EdgeTableNode)
            tempNode.weight = weight
            tempNode.index = endVert
            tempNode.edgeTableNode = nil
            this.vertNode[startVert].edgeTableNode = tempNode
            continue
        }

        for edgeNode != nil {
            // 单链表尾插节点
            if edgeNode.edgeTableNode == nil {
                tempNode := new(EdgeTableNode)
                tempNode.weight = weight
                tempNode.index = endVert
                tempNode.edgeTableNode = nil
                edgeNode.edgeTableNode = tempNode
                break
            }

            edgeNode = edgeNode.edgeTableNode
        }
    }
}


// 初始化队列
var queue *Queue = NewQueue()

type MapParent struct {
    parent int
    son    int
}



// 拓扑排序
func (this *Graph) TuoPuSort() ([]int, bool) {
    mapQianXIang := make(map[int]int)
    sortList := []int{}

    for i := 0; i < this.vertNum; i++ {
        node := this.vertNode[i]
        if node == nil {
            continue
        }

        // 获取邻接链表
        edgeNode := node.edgeTableNode
        // 记录每个子节点的被指向的次数
        for edgeNode != nil {
            mapQianXIang[edgeNode.index]++
            edgeNode = edgeNode.edgeTableNode
        }

    }

    // 拓扑排序就是把没有前项节点的先放入队列中
    for i := 0; i < this.vertNum; i++ {
        if mapQianXIang[i] == 0 {
            queue.Put(MapParent{parent: -1, son: i})
        }
    }

    for !queue.IsEmpty() {
        node := queue.Pop()

        sortList = append(sortList, node.value.son)

        // 获取邻接链表
        edgeNode := this.vertNode[node.value.son].edgeTableNode
        // 递减指向次数 其实就是斩断的过程
        for edgeNode != nil {
            mapQianXIang[edgeNode.index]--
            if mapQianXIang[edgeNode.index] == 0 {
                // 忽略parent 我们son
                queue.Put(MapParent{parent: -1, son: edgeNode.index})
            }
            edgeNode = edgeNode.edgeTableNode
        }
    }

    fmt.Println(sortList, "  ", len(sortList) != this.vertNum)
    // bool 表示有没有环
    return sortList, len(sortList) != this.vertNum
}


func GrapTuoPuSort() {
    var grap = NewGraph(5, 6, 1)
    grap.InitGrap()

    grap.TuoPuSort()
}

func main() {
    //GrapDfs()
    GrapTuoPuSort()
    //GrapBfs()
}

时间复杂度: O(n + e),其中n为图中的结点数目,e为图中的边的数目
空间复杂度:O(n)--队列的额外空间

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,445评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,889评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,047评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,760评论 1 276
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,745评论 5 367
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,638评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,011评论 3 398
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,669评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,923评论 1 299
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,655评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,740评论 1 330
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,406评论 4 320
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,995评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,961评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,197评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,023评论 2 350
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,483评论 2 342

推荐阅读更多精彩内容