二叉树的锯齿形层序遍历( LeetCode 103 )

一、题目描述

给定一个二叉树,返回其节点值的锯齿形层序遍历。(即先从左往右,再从右往左进行下一层遍历,以此类推,层与层之间交替进行)。

例如:
给定二叉树 [3,9,20,null,null,15,7],

    3
   / \
  9  20
    /  \
   15   7

返回锯齿形层序遍历如下:

[
  [3],
  [20,9],
  [15,7]
]

二、题目解析

该问题需要用到队列,与之前的二叉树的层次遍历类似,不同点在于在偶数层需要翻转一下。

  • 建立一个 queue
  • 先把根节点放进去,这时候找根节点的左右两个子节点
  • 去掉根节点,此时 queue 里的元素就是下一层的所有节点
  • 循环遍历,将结果存到一个一维向量里
  • 遍历完之后再把这个一维向量存到二维向量里
  • 如果该层为偶数层,则 reverse 翻转一下
  • 以此类推,可以完成层序遍历

三、参考代码

1、Java 代码

// 登录 AlgoMooc 官网获取更多算法图解
// https://www.algomooc.com
// 作者:程序员吴师兄
// 代码有看不懂的地方一定要私聊咨询吴师兄呀
// 二叉树的锯齿形层序遍历( LeetCode 103 ):https://leetcode-cn.com/problems/binary-tree-zigzag-level-order-traversal/
class Solution {
    public List<List<Integer>> zigzagLevelOrder(TreeNode root) {

        // 设置 res 用来保存输出结果
        List<List<Integer>> res = new LinkedList<List<Integer>>();

        // 边界情况处理
        if(root == null) return res;

        // 设置一个队列,用来存储二叉树中的元素
        Queue<TreeNode> nodeQueue = new LinkedList<TreeNode>();

        // 队列添加二叉树的根节点
        nodeQueue.add(root);

        // 是否从左至右标志位
        boolean isOrderLeft = true;

        // 遍历队列,直到队列为空,说明访问了二叉树中所有的节点
        while (!nodeQueue.isEmpty()) {

            // 用来记录 queue 的长度,即每层节点的个数
            int size = nodeQueue.size();

            // 用来保存每一层节点,保存成功后添加到 res 中
            // 由于需要既可以在队头添加元素、有需要可以在队尾添加元素,因此使用双端队列
            // 用双端队列是方便调换方向存储不同层的元素
            Deque<Integer> levelList = new LinkedList<Integer>();

            // 使用 for 循环,将 nodeQueue 中的元素添加的 levelList 中
            // 按标志位存入双端队列,再按固定顺序读取下一层节点值进队列
            for (int i = 0; i < size; ++i) {

                // 从 queue 中取出一个节点,此时,nodeQueue 已经抛出了这个节点         
                TreeNode curNode = nodeQueue.poll();

                // 先从左往右存储,每次存储在 levelList 的队尾,即 addLast
                if (isOrderLeft) {
                    levelList.addLast(curNode.val);

                // 再从右往左存储,每次存储在 levelList 的队头,即 addFirst
                } else {
                    levelList.addFirst(curNode.val);
                }

                // 将下一层节点值进入队列,如果有,按照从左到右的顺序
                // 判断当前节点的左子节点是否有值,如果有,则添加到 nodeQueue 中
                if (curNode.left != null) {
                    nodeQueue.add(curNode.left);
                }

                // 判断当前节点的右子节点是否有值,如果有,则添加到 nodeQueue 中    
                if (curNode.right != null) {
                    nodeQueue.add(curNode.right);
                }
            }

            // 把存放了每一层元素的数组 levelList 添加到 res 中,注意格式转换,需要转换为数组的形式
            res.add(new LinkedList<Integer>(levelList));

            // 【从左到右】和【从右到左】的顺序来回交替
            isOrderLeft = !isOrderLeft;
        }
        // 返回 res
        return res;
    }
}

2、C++ 代码

// 登录 AlgoMooc 官网获取更多算法图解
// https://www.algomooc.com
// 作者:程序员吴师兄
// 代码有看不懂的地方一定要私聊咨询吴师兄呀
// 二叉树的锯齿形层序遍历( LeetCode 103 ):https://leetcode-cn.com/problems/binary-tree-zigzag-level-order-traversal/
class Solution {
public:
    vector<vector<int>> zigzagLevelOrder(TreeNode* root) {

        // 设置 res 用来保存输出结果
        vector<vector<int>> res;

        // 边界情况处理
        if(root == NULL) return res;

        // 设置一个队列,用来存储二叉树中的元素
        queue<TreeNode*> nodeQueue;

        // 队列添加二叉树的根节点
        nodeQueue.push(root);

        // 是否从左至右标志位
        bool isOrderLeft = true;

        // 遍历队列,直到队列为空,说明访问了二叉树中所有的节点
        while (!nodeQueue.empty()) {

            // 用来记录 queue 的长度,即每层节点的个数
            int size = nodeQueue.size();

            // 用来保存每一层节点,保存成功后添加到 res 中
            // 由于需要既可以在队头添加元素、有需要可以在队尾添加元素,因此使用双端队列
            // 用双端队列是方便调换方向存储不同层的元素
            deque<int> levelList;

            // 使用 for 循环,将 nodeQueue 中的元素添加的 levelList 中
            // 按标志位存入双端队列,再按固定顺序读取下一层节点值进队列
            for (int i = 0; i < size; ++i) {

                // 从 queue 中取出一个节点,此时,nodeQueue 已经抛出了这个节点         
                TreeNode* curNode = nodeQueue.front();
                nodeQueue.pop();

                // 先从左往右存储,每次存储在 levelList 的队尾,即 addLast
                if (isOrderLeft) {
                    levelList.push_back(curNode->val);

                // 再从右往左存储,每次存储在 levelList 的队头,即 addFirst
                } else {
                    levelList.push_front(curNode->val);
                }

                // 将下一层节点值进入队列,如果有,按照从左到右的顺序
                // 判断当前节点的左子节点是否有值,如果有,则添加到 nodeQueue 中
                if (curNode->left != NULL) {
                    nodeQueue.push(curNode->left);
                }

                // 判断当前节点的右子节点是否有值,如果有,则添加到 nodeQueue 中    
                if (curNode->right != NULL) {
                    nodeQueue.push(curNode->right);
                }
            }
            vector<int>v;
            while(!levelList.empty()){
                int element = levelList.front();
                levelList.pop_front();
                v.push_back(element);
            }
            // 把存放了每一层元素的数组 levelList 添加到 res 中,注意格式转换,需要转换为数组的形式
            res.push_back(v);

            // 【从左到右】和【从右到左】的顺序来回交替
            isOrderLeft = !isOrderLeft;
        }
        // 返回 res
        return res;
    }
};

3、Python 代码

# 登录 AlgoMooc 官网获取更多算法图解
# https://www.algomooc.com
# 作者:程序员吴师兄
# 代码有看不懂的地方一定要私聊咨询吴师兄呀
# 二叉树的锯齿形层序遍历( LeetCode 103 ):https://leetcode-cn.com/problems/binary-tree-zigzag-level-order-traversal/
class Solution:
    def zigzagLevelOrder(self, root: TreeNode) -> List[List[int]]:

        # 设置 res 用来保存输出结果
        res = [ ]

        # 边界情况处理
        if not root :
           return res

        # 设置一个队列,用来存储二叉树中的元素
        nodeQueue = deque()

        # 队列添加二叉树的根节点
        nodeQueue.append(root)

        # 是否从左至右标志位
        isOrderLeft = True

        # 遍历队列,直到队列为空,说明访问了二叉树中所有的节点
        while nodeQueue :

            # 用来记录 queue 的长度,即每层节点的个数
            size = len(nodeQueue)

            # 用来保存每一层节点,保存成功后添加到 res 中
            # 由于需要既可以在队头添加元素、有需要可以在队尾添加元素,因此使用双端队列
            # 用双端队列是方便调换方向存储不同层的元素
            levelList = collections.deque([])

            # 使用 for 循环,将 nodeQueue 中的元素添加的 levelList 中
            # 按标志位存入双端队列,再按固定顺序读取下一层节点值进队列
            for _ in range(size) :

                # 从 queue 中取出一个节点,此时,nodeQueue 已经抛出了这个节点         
                curNode =  nodeQueue.popleft()

                # 先从左往右存储,每次存储在 levelList 的队尾,即 appendLast
                if isOrderLeft :
                    levelList.append(curNode.val)

                # 再从右往左存储,每次存储在 levelList 的队头,即 appendFirst
                else :
                   levelList.appendleft(curNode.val)


                # 将下一层节点值进入队列,如果有,按照从左到右的顺序
                # 判断当前节点的左子节点是否有值,如果有,则添加到 nodeQueue 中
                if curNode.left :
                    nodeQueue.append(curNode.left)


                # 判断当前节点的右子节点是否有值,如果有,则添加到 nodeQueue 中    
                if curNode.right :
                    nodeQueue.append(curNode.right)


            # 把存放了每一层元素的数组 levelList 添加到 res 中,注意格式转换,需要转换为数组的形式
            res.append(list(levelList))

            # 【从左到右】和【从右到左】的顺序来回交替
            isOrderLeft = not isOrderLeft

        # 返回 res
        return res

四、动画理解(没有声音)