Bootstrap

算法套路九——二叉树广度优先遍历(层序遍历)

算法套路九——二叉树广度优先遍历(层序遍历)

算法示例LeetCode102. 二叉树的层序遍历

给你二叉树的根节点 root ,返回其节点值的 层序遍历 。 (即逐层地,从左到右访问所有节点)。
在这里插入图片描述

法一:双数组

在层序遍历时采用两个数组来记录,如下图所示,cur记录当前结点,通过遍历cur将cur中的节点的子结点放入nxt中,同时将cur值记录在vlas中,之后令cur=nxt重新遍历cur。
在这里插入图片描述
在这里插入图片描述
cur为空时即所有结点遍历完成,退出循环。

class Solution:
    def levelOrder(self, root: Optional[TreeNode]) -> List[List[int]]:
        if root is None:
            return []
        cur=[root]
        ans=[]
        while cur:
            nxt=[]
            vals=[]
            for node in cur:
                vals.append(node.val)
                if node.left: nxt.append(node.left)
                if node.right:nxt.append(node.right)
            cur=nxt
            ans.append(vals)
        return ans

法二:队列

与两个数组类似,队列中首先存当前遍历的节点,之后根据当前队列的长度遍历队列,将每个节点的左右子结点加入队列中,并将队列最左侧元素出队。

class Solution:
    def levelOrder(self, root: Optional[TreeNode]) -> List[List[int]]:
        if root is None:
            return []
        q=deque([root])
        ans=[]
        while q:
            vals=[]
            for _ in range(len(q)):
                node=q.popleft()
                vals.append(node.val)
                if node.left: q.append(node.left)
                if node.right:q.append(node.right)
            ans.append(vals)
        return ans

这两种方法只有细微的地方有代码的区别,故可以选一种自己认为比较顺手的作为常用解法

算法练习一:LeetCode103. 二叉树的锯齿形层序遍历

给你二叉树的根节点 root ,返回其节点值的 锯齿形层序遍历 。(即先从左往右,再从右往左进行下一层遍历,以此类推,层与层之间交替进行)。在这里插入图片描述

本题与上题类似,只是需要增加一个bool变量even记录当前层数是否为偶数,若为偶数,则需要在将值添加进vals进行反向添加,即vals[len(cur)-1-i]=node.Val

法一:双数组

func zigzagLevelOrder(root *TreeNode) (ans [][]int) {
    if root==nil{
        return 
    }
    cur:=[]*TreeNode{root}
    even:=false
    for len(cur)>0{
        nxt:=[]*TreeNode{}
        vals:=make([]int,len(cur))
        for i,node:=range cur{
            if even{vals[len(cur)-1-i]=node.Val}else{vals[i]=node.Val }
            if node.Left != nil {nxt  = append(nxt , node.Left)}
            if node.Right != nil { nxt  = append(nxt , node.Right)}
        }
        cur=nxt
        even = !even
        ans=append(ans,vals)
    }
    return
}

法二:队列

func zigzagLevelOrder(root *TreeNode) (ans [][]int) {
    if root==nil{
        return 
    }
    q:=[]*TreeNode{root}
    even:=false
    for len(q)>0{
        n:=len(q)
        vals:=make([]int,n)
        for i:=0;i<n;i++{//对遍历变量有修改时,不能使用for range遍历
            node:=q[0]
            q=q[1:]
            if even{vals[n-1-i]=node.Val}else{vals[i]=node.Val }
            if node.Left != nil {q = append(q, node.Left)}
            if node.Right != nil { q = append(q, node.Right)}
        }
        ans=append(ans,vals)
        even=!even
    }
    return
}

算法练习二:LeetCode104. 二叉树的最大深度

给定一个二叉树,找出其最大深度。
在这里插入图片描述

本题在之前使用了递归解决,这里使用广度优先层序遍历队列来解决

法一:双数组

func maxDepth(root *TreeNode)  int {
    if root==nil{
        return 0
    }
    cur:=[]*TreeNode{root}
    maxD:=0
    for len(cur)!=0{
        nxt:=[]*TreeNode{}
        maxD++
        for _,node:=range cur{
            if node.Left!=nil{nxt=append(nxt,node.Left)}
            if node.Right!=nil{nxt=append(nxt,node.Right)}
        }
        cur=nxt 
    }
    return maxD
}

法二:队列

func maxDepth(root *TreeNode)  int {
    if root==nil{
        return 0
    }
    q:=[]*TreeNode{root}
    maxD:=0
    for len(q)!=0{
        maxD++
        n:=len(q)
        for i:=0;i<n;i++{
            node:=q[0]
            q=q[1:]
            if node.Left!=nil{q=append(q,node.Left)}
            if node.Right!=nil{q=append(q,node.Right)}
        }
        
    }
    return maxD
}

算法练习三:LeetCode111. 二叉树的最小深度

给定一个二叉树,找出其最小深度。
在这里插入图片描述

使用广度优先搜索的方法遍历整棵树。
当我们找到一个叶子节点时,直接返回这个叶子节点的深度。层序遍历的性质保证了最先搜索到的叶子节点的深度一定最小。

func minDepth(root *TreeNode) int {
    if root==nil{
        return 0
    }
    minD:=0
    q:=[]*TreeNode{root}
    for len(q)!=0{
        minD++
        n:=len(q)
        for i:=0;i<n;i++{
            node:=q[0]
            q=q[1:]
            if node.Left==nil&&node.Right==nil{return minD}
            if node.Left!=nil{q=append(q,node.Left)}
            if node.Right!=nil{q=append(q,node.Right)}
            }
        }
        return -1
}

算法进阶一:LeetCode513. 找树左下角的值

给定一个二叉树的 根节点 root,请找出该二叉树的 最底层 最左边 节点的值。假设二叉树中至少有一个节点。
在这里插入图片描述

此题有一定难度,因为是要找到最底层最左边节点的值,想到层序遍历,那么如何得到最左边的节点呢,我们可以在进行层序遍历时每次都从右往左入队,这样每次都是上层、右边的节点先出队,故最底层最左边的节点最后出队

func findBottomLeftValue(root *TreeNode) int {
    q:=[]*TreeNode{root}
    var node *TreeNode
    for len(q)>0{
        node=q[0]
        q=q[1:]
        if node.Right!=nil{
            q=append(q,node.Right)
        }
        if node.Left!=nil{
            q=append(q,node.Left)
        }
    }
    return node.Val
}

算法进阶二:LeetCode199. 二叉树的右视图

给定一个二叉树的 根节点 root,想象站在它的右侧,按照从顶部到底部的顺序,返回从右侧能看到的节点值。
在这里插入图片描述

此题之前使用深度遍历递归时有一定难度,但如果使用广度优先的层序遍历,则很明显,只需要记录队列每一层的最后一个结点即可

func rightSideView(root *TreeNode) []int {
    if root==nil{
        return []int{}
    }
    ans:=[]int{}
    q:=[]*TreeNode{root}
    for len(q)!=0{
        n:=len(q)
        for i:=0;i<n;i++{
            node:=q[0]
            q=q[1:]
            if node.Left!=nil{q=append(q,node.Left)}
            if node.Right!=nil{q=append(q,node.Right)}
            if i==n-1{
                ans=append(ans,node.Val)
            }
        }   
    }
    return ans
}
;