深广度优先遍历一般情况下我们都是用在复杂的图结构,以方便我们进行必要的检索操作。之前我用C++,Python写过图的深广度优先遍历,今天在这里想用go语言来实现这一操作,并希望对各位有帮助

邻接表

图的存贮结构很多,在这里我们选择中等难度的邻接表作为我们图的储存结构,下面的图是我模拟的一个图结构,是一个有向非连通图(F点未相连)来作为这次深广度遍历的实验图。


未命名文件.png

下面附上邻接表的代码(含注释)

package main

import "fmt"

//邻接表我们需要把顶点和边都看成是一个结构节点
//所以我们在下面创建这两种结构

//VertexNode 顶点节点结构体
type VertexNode struct {
    data     string   //存放顶点数据
    firstarc *ArcNode //顶点的第一条边
}

//ArcNode 边节点结构体
type ArcNode struct {
    adjvex int      //存放顶点的下标
    next   *ArcNode //存放下一条边节点
}

//GraphAdjacencyList 图的邻接表结构体
type GraphAdjacencyList struct {
    vertexNode []VertexNode //存放顶点的数组
    visted     []bool       //是否遍历过的bool类型数组
}

// 添加顶点 
func (g *GraphAdjacencyList) addVertexNode(data string) bool {
    
    //定义一个顶点节点实例,并储存顶点的数据
    temp := VertexNode{}
    temp.data = data
    
    //判断这个顶点是否已经存在
    for _, v := range g.vertexNode {
        if v.data == data {
            return false
        }
    }
    //把顶点节点存入切片中,方便我们后面遍历使用
    g.vertexNode = append(g.vertexNode, temp)
    
    //初始化访问切片为false,代表这个顶点没有被访问过
    g.visted = append(g.visted, false) 
    return true
}

//添加边节点
func (g *GraphAdjacencyList) addArcNode(verNode1, verNode2 string) bool {
    //获得该边节点两头顶点在切片中的下标
    index1 := g.verNodeIndex(verNode1)
    index2 := g.verNodeIndex(verNode2)
    
    //判断顶点是否存在或重复
    if index1 == -1 || index2 == -1 || index1 == index2 {
        return false
    }

    //进行顶点和边节点的逻辑连接
    temp := new(ArcNode)    //创建边节点实例
    
    temp.adjvex = index2    //对边节点和顶点进行绑定
    temp.next = g.vertexNode[index1].firstarc    //设置该边指向的下一条边
    g.vertexNode[index1].firstarc = temp    
    
    //同理设置另一个顶点
    temp = new(ArcNode)
    temp.adjvex = index1
    temp.next = g.vertexNode[index2].firstarc
    g.vertexNode[index2].firstarc = temp
    return true
}

// 获得顶点的下标
func (g *GraphAdjacencyList) verNodeIndex(data string) int {
    for i, v := range g.vertexNode {
        if v.data == data {
            return i
        }
    }
    return -1
}

//初始化访问切片visted
func (g *GraphAdjacencyList) initVisted() {
    for i, _ := range g.visted {
        g.visted[i] = false
    }
}

func main() {
    g := GraphAdjacencyList{} //定义一个邻接表实例

    //添加四个顶点
    topData := [5]string{"A", "B", "C", "D", "E"}
    for _, v := range topData {
        if g.addVertexNode(v) == false {
            fmt.Println("顶点数据重复")
        }
    }
    //添加五条边
    g.addArcNode("A", "B")
    g.addArcNode("A", "C")
    g.addArcNode("A", "D")
    g.addArcNode("B", "D")
    g.addArcNode("C", "D")
    g.addArcNode("D", "E")
}



深度优先遍历

深度优先遍历(Depth-First-Search),是搜索算法的一种,它沿着树的深度遍历树的节点,尽可能深地搜索树的分支。当节点v的所有边都已被探寻过,将回溯到发现节点v的那条边的起始节点。这一过程一直进行到已探寻源节点到其他所有节点为止,如果还有未被发现的节点,则选择其中一个未被发现的节点为源节点并重复以上操作,直到所有节点都被探寻完成。

简单的说,DFS就是从图中的一个节点开始追溯,直到最后一个节点,然后回溯,继续追溯下一条路径,直到到达所有的节点,如此往复,直到没有路径为止。


未命名文件(1).png

例如上图,如果从A开始则会按照我标的数字顺序进行遍历:A->C(因为C点还有连通点,所以会去D而不返回A去B)->D(同上理)->E->B->F

深度优先遍历的代码

//开始DFS
func (g *GraphAdjacencyList) travelDFS(data string) bool {
    index := g.verNodeIndex(data)
    if index == -1 {
        return false
    }
    g.DFS(index)
    // 通过循环来达到遍历所有顶点的目的,防止非连同图无法充分遍历的情况
    for i, _ := range g.vertexNode {
        if g.visted[i] == false {
            g.DFS(i)
        }
    }
    g.initVisted() //初始化访问数组
    return true
}

//DFS 深度优先遍历
func (g *GraphAdjacencyList) DFS(index int) {
    fmt.Println(g.vertexNode[index].data)
    g.visted[index] = true
    
    //获得该顶点的第一条边节点,通过这个边节点找到另一头的顶点
    p := g.vertexNode[index].firstarc
    for p != nil {
        u := p.adjvex
        
        //如果顶点没有被访问,递归遍历
        if g.visted[u] == false {
            g.DFS(u)
        }
        //边节点后移
        p = p.next
    }
}

广度优先遍历

广度优先遍历(Breadth-First-Search)是从根节点开始,沿着图的宽度遍历节点,如果所有节点均被访问过,则算法终止,BFS 同样属于盲目搜索,一般用队列数据结构来辅助实现BFS。
未命名文件(2).png

例如上图,如果从A开始则会按照我标的数字顺序进行遍历:A->C(因为A点还有连通点,所以会返回A去B而不通过C去D)->B(A的所有连通点都被访问过同所以通过B顶点继续访问)->D->E->F

广度优先遍历的代码

//因为我们要使用队列这个结构来辅助我们进行广度优先遍历
//所以在这里模拟一个队列出来

//QNode    队列节点结构体
type QNode struct {
    data int    //用来存放顶点的下标
    next *QNode //指向下一个节点
}

//Queue 队列结构体
type Queue struct {
    front  *QNode //指向队列头的指针
    rear   *QNode //指向队列尾的指针
    length int    //队列的长度
}

//初始化队列
func (q *Queue) initQueue() {
    q.front = new(QNode)
    q.rear = new(QNode)
    q.length = 0
    q.front = q.rear
    q.front.next = nil
}

//入队
func (q *Queue) enQueue(data int) {
    p := new(QNode)
    p.data = data
    p.next = nil
    q.rear.next = p
    q.rear = p
    q.length += 1
}

//出队
func (q *Queue) deQueue() int {
    q.length -= 1
    p := q.front.next
    e := p.data
    q.front.next = p.next
    if q.rear == p {
        q.rear = q.front
    }
    return e
}

//判断队列是否为空
func (q *Queue) judgeEmpty() bool {
    if q.length == 0 {
        return true
    } else {
        return false
    }
}

//开始BFS
func (g *GraphAdjacencyList) travelBFS(data string) bool {
    //获得开始访问顶点的下标
    index := g.verNodeIndex(data)
    if index == -1 {
        return false
    }
    
    //开始BFS
    g.BFS(index)
    // 通过循环来达到遍历所有顶点的目的,防止非连同图无法充分遍历的情况
    for i, _ := range g.vertexNode {
        if g.visted[i] == false {
            g.BFS(i)
        }
    }
    g.initVisted() //初始化访问数组
    return true
}

//BFS
func (g *GraphAdjacencyList) BFS(index int) {
    //创建队列的实例
    q := new(Queue)
    
    //初始化队列
    q.initQueue()
    fmt.Println(g.vertexNode[index].data)
    g.visted[index] = true
    //访问过的顶点入队
    q.enQueue(index)
    
    //队列不为空
    for q.judgeEmpty() == false {
        //出队找未遍历的顶点
        v := q.deQueue()
        //创建边实例,找顶点
        p := g.vertexNode[v].firstarc
        for p != nil {
            //如果未访问输出顶点值,入队
            if g.visted[p.adjvex] == false {
                fmt.Println(g.vertexNode[p.adjvex].data)
                g.visted[p.adjvex] = true
                q.enQueue(p.adjvex)
            }
            //边节点后移
            p = p.next
        }
    }
}

完整代码

详细注释在上面,这里只是一提供一个完整的深广度遍历代码

package main

import "fmt"

// //QNode 邻接表节点结构体
// type QNode struct {
//     Data string //存放节点的数据
//     Next *QNode //存放下一个节点的指针
// }

//QNode    队列节点结构体
type QNode struct {
    data int    //用来存放顶点的下标
    next *QNode //指向下一个节点
}

//Queue 队列结构体
type Queue struct {
    front  *QNode //指向队列头的指针
    rear   *QNode //指向队列尾的指针
    length int    //队列的长度
}

//VertexNode 顶点结点结构体
type VertexNode struct {
    data     string   //存放顶点数据
    firstarc *ArcNode //顶点的第一条边
}

//ArcNode 边节点结构体
type ArcNode struct {
    adjvex int      //存放顶点的下标
    next   *ArcNode //存放下一条边节点
}

//GraphAdjacencyList 图的邻接表结构体
type GraphAdjacencyList struct {
    vertexNode []VertexNode //存放顶点的数组
    visted     []bool       //是否遍历过的bool类型数组
}

//初始化队列
func (q *Queue) initQueue() {
    q.front = new(QNode)
    q.rear = new(QNode)
    q.length = 0
    q.front = q.rear
    q.front.next = nil
}

//入队
func (q *Queue) enQueue(data int) {
    p := new(QNode)
    p.data = data
    p.next = nil
    q.rear.next = p
    q.rear = p
    q.length += 1
}

//出队
func (q *Queue) deQueue() int {
    q.length -= 1
    p := q.front.next
    e := p.data
    q.front.next = p.next
    if q.rear == p {
        q.rear = q.front
    }
    return e
}

//判断队列是否为空
func (q *Queue) judgeEmpty() bool {
    if q.length == 0 {
        return true
    } else {
        return false
    }
}

// 添加顶点
func (g *GraphAdjacencyList) addVertexNode(data string) bool {
    temp := VertexNode{}
    temp.data = data
    for _, v := range g.vertexNode {
        if v.data == data {
            return false
        }
    }
    g.vertexNode = append(g.vertexNode, temp)
    g.visted = append(g.visted, false) //初始化为false代表该节点没有被访问
    return true
}

//添加边节点
func (g *GraphAdjacencyList) addArcNode(verNode1, verNode2 string) bool {
    index1 := g.verNodeIndex(verNode1)
    index2 := g.verNodeIndex(verNode2)
    if index1 == -1 || index2 == -1 || index1 == index2 {
        return false
    }

    //进行顶点和边节点的逻辑连接
    temp := new(ArcNode)
    temp.adjvex = index2
    temp.next = g.vertexNode[index1].firstarc
    g.vertexNode[index1].firstarc = temp

    temp = new(ArcNode)
    temp.adjvex = index1
    temp.next = g.vertexNode[index2].firstarc
    g.vertexNode[index2].firstarc = temp
    return true
}

// 获得顶点的下标
func (g *GraphAdjacencyList) verNodeIndex(data string) int {
    for i, v := range g.vertexNode {
        if v.data == data {
            return i
        }
    }
    return -1
}

//初始化访问切片visted
func (g *GraphAdjacencyList) initVisted() {
    for i, _ := range g.visted {
        g.visted[i] = false
    }
}

//开始DFS
func (g *GraphAdjacencyList) travelDFS(data string) bool {
    index := g.verNodeIndex(data)
    if index == -1 {
        return false
    }
    g.DFS(index)
    // 通过循环来达到遍历所有顶点的目的
    for i, _ := range g.vertexNode {
        if g.visted[i] == false {
            g.DFS(i)
        }
    }
    g.initVisted() //初始化访问数组
    return true
}

//DFS 深度优先遍历
func (g *GraphAdjacencyList) DFS(index int) {
    fmt.Println(g.vertexNode[index].data)
    g.visted[index] = true
    p := g.vertexNode[index].firstarc
    for p != nil {
        u := p.adjvex
        if g.visted[u] == false {
            g.DFS(u)
        }
        p = p.next
    }
}

//开始BFS
func (g *GraphAdjacencyList) travelBFS(data string) bool {
    index := g.verNodeIndex(data)
    if index == -1 {
        return false
    }
    g.BFS(index)
    for i, _ := range g.vertexNode {
        if g.visted[i] == false {
            g.BFS(i)
        }
    }
    g.initVisted() //初始化访问数组
    return true
}

//BFS
func (g *GraphAdjacencyList) BFS(index int) {
    q := new(Queue)
    q.initQueue()
    fmt.Println(g.vertexNode[index].data)
    g.visted[index] = true
    q.enQueue(index)
    for q.judgeEmpty() == false {
        v := q.deQueue()
        p := g.vertexNode[v].firstarc
        for p != nil {
            if g.visted[p.adjvex] == false {
                fmt.Println(g.vertexNode[p.adjvex].data)
                g.visted[p.adjvex] = true
                q.enQueue(p.adjvex)
            }
            p = p.next
        }
    }
}
func main() {
    g := GraphAdjacencyList{} //定义一个邻接表

    //添加四个顶点
    topData := [6]string{"A", "B", "C", "D", "E", "F"}
    for _, v := range topData {
        if g.addVertexNode(v) == false {
            fmt.Println("顶点数据重复")
        }
    }
    //添加五条边
    g.addArcNode("A", "B")
    g.addArcNode("A", "C")
    g.addArcNode("B", "D")
    g.addArcNode("C", "D")
    g.addArcNode("D", "E")

    //深度优先遍历
    fmt.Println("深度优先遍历")
    g.travelDFS("A")
    //广度优先遍历
    fmt.Println("广度优先遍历")
    g.travelBFS("A")
}
Last modification:April 17th, 2020 at 03:59 pm
如果觉得我的文章对你有用,请随意赞赏