算法7 --- 图 (如何宽度、深度遍历一张图)

119 阅读3分钟

现有一张图

image.png

宽度优先遍历的顺序可以是

1 2 3 4 5

我们创建一个队列和已打印节点map

把1放进去,打印1,标记1已打印

image.png

然后进入循环,直到队列为空

从队列中弹出1,判断1的next 2 3 4 有没有打印过,没有打印过,就打印 2 3 4 ,并标记2 3 4 已打印

image.png

弹出2,判断2 的next 3 5有没有打印过,3打印过就跳过,5没打印过就打印5,将5放进队列

image.png

以此类推


func bts(node *Node) {
   if node == nil {
      return
   }
   print := make(map[*Node]struct{})

   queue := make([]*Node, 0)
   queue = append(queue, node)
   print[node] = struct{}{}
   for len(queue) != 0 {
      //队列弹出一个值
      cur := queue[0]
      queue = queue[1:]


      //将它的所有next放进去队列
      for _, next := range cur.Next {
         if _, exist := print[next]; !exist {
            queue = append(queue, next)
            
            //打印该值
            fmt.Println(cur.value)
            //沉没该值
            print[cur] = struct{}{}
         }
      }
   }
}

深度优先遍历的顺序可以是1 2 3 5 4 (一条路走到黑)

我们创建一个栈(先进后出),和一个已打印节点的map

第一步将1节点放进去,打印1,标记已打印

image.png

进入循环,直到栈为空

弹出1,获取1的next 2 3 4,2如果未被标记过,打印2,标记2,并将当前节点1和2再压进栈中,退出子循环(不处理3 和4)

image.png

弹出2,获取2的next 3 5 ,3 如果未被标记过,打印3, 标记3,并将当前节点2和3再压进栈中,退出子循环(不处理5)

image.png

弹出3,获取3的next5 ,5 如果未被标记过,打印5, 标记5,并将当前节点3和5再压进栈中

image.png

弹出5,5没有next,不处理

image.png

弹出3,3的next5已经标记过,不处理

image.png

弹出2,2的next3 5 已经标记过,不处理

image.png

弹出1,1的next 2 3 4 ,23不处理,打印4,标记4,将当前节点1和4再压进栈

image.png

弹出4,4没有next不处理

image.png

弹出1,1的next都被标记过不处理

image.png

栈空退出循环


func dfs(node *Node) {
   if node == nil {
      return
   }
   stack := make([]*Node, 0)
   print := make(map[*Node]struct{})

   stack = append(stack, node)
   print[node] = struct{}{}

   fmt.Println(node.value)

   for len(stack) != 0 {
      cur := stack[len(stack)-1]
      stack = stack[:len(stack)-1]

      for _, next := range cur.Next {
         if _, exist := print[next]; !exist {
            stack = append(stack, cur)
            stack = append(stack, next)
            print[next] = struct{}{}

            fmt.Println(next.value)
            break
         }
      }
   }

}

image.png

现在有个项目1需要编译,但是它依赖项目2 3 4 ,同时要编译2就要先编译5,我们如何找到正确的编译顺序呢?

这里用到拓扑排序

新建一个队列queue,将所有入度为0的点放入队列

image.png

进入循环直到队列为空

弹出5,编译5,再把5和5的next边擦掉

image.png

擦掉后2的入度为0了,将2放入队列

image.png

弹出4,编译4,再把4和4的next边擦掉

image.png

弹出2,编译2,再把2和2的next边擦掉

image.png

擦掉后,3的入度为0,将3放入队列

image.png

弹出3,编译3,再把3和3的next边擦掉

image.png

擦掉后,1的入度为0,将1放入队列

image.png

弹出1,编译1,队列为空退出循环


func sortedTopology(graph Graph){
   inMap:=make(map[*Node]int)
   //准备入度为0的队列
   queue:=make([]*Node,0)
   
   for _,node:=range graph.nodes{
      inMap[node]=node.in
      
      if node.in==0{
         queue= append(queue, node)
      }
   }
   
   
   for len(queue)!=0{
      cur:=queue[0]
      queue=queue[1:]
      
      //编译cur
      fmt.Println(cur.value)
      
      for _,next:=range cur.Next{
         inMap[next]=inMap[next]-1
         if inMap[next]==0{
            queue= append(queue, next)
         }
      }
   }
}