二叉树的之字形层序遍历

二叉树的之字形层序遍历

1、参考资料

https://www.nowcoder.com/practice/47e1687126fa461e8a3aff8632aa5559

2、题目要求

题目描述

给定一个二叉树,返回该二叉树的之字形层序遍历,(第一层从左向右,下一层从右向左,一直这样交替)
例如:给定的二叉树是 {3,9,20,#,#,15,7}

image-20200905002707893

该二叉树之字形层序遍历的结果是

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

示例

输入

{1,#,2}

输出

[[1],[2]]

3、代码思路

基于双栈的层序遍历

  1. 我们可以使用 queue 对二叉树进行层序遍历,现在要求进行之字形层序遍历,即第一层正序输出,第二层逆序输出,第三层又正序输出,以此类推。。。
  2. 但凡牵涉到逆序,我们就会想到栈这个数据结构,因为栈先进后出,先进入栈的数据最后出栈,所以栈能实现逆序的效果
  3. 起初写代码的时候我卡壳了,因为我只用了一个栈,抠破脑袋也捋清思路,现在想想,这不挺简单的一个道理嘛~~~
  4. 假设我们现在遍历到二叉树第二层,首先,为了实现逆序遍历,二叉树第二层的节点肯定都保存在一个栈中(stack1),这是毋庸置疑的,那么下一层节点咋办呢?肯定不能往 stack1 里面塞,因为塞了之后就把 stack1 的栈顶给封住了,第二层剩余的节点就取不出来了,所以我们还需要一个栈(stack2)存储下一层的节点
  5. 在经历上述操作后,stack1 变为了空栈,stack2 存储了第三层的节点,然后我们接着遍历 stack2,将第四层的节点存入 stack1
  6. 仔细想了想,我这里的设计思路还有点牛逼(自恋一下),引用 JVM 幸存者区的一句话:谁空谁是 to,这点在我代码中也有体现,stackOut 表示存储当前层节点的栈,stackIn 表示存储下一层节点的栈,stack1stack2 的身份在 stackOutstackIn 之间不停地切换
  7. 关于遍历的方向:从左往右遍历(正向遍历)时,我们需要先将当前节点的左节点 stackIn 中;从右往左遍历(反向遍历)时,我们需要先将当前节点的右节点 stackIn

举例说明

  1. 以下面这颗二叉树举例说明,先将根节点 1压入 stack1

          1 
         / \
        2    3
       /      \
      4        5
    
  2. 首先遍历的方向为正向遍历(isForward = true,从左往右遍历)时

    1. 此时 stack1stackOutstack2stackIn,遍历完成后 stack1 为空,stack2 中存储着第二层的节点
    2. 遍历时不断 pop()stackOut 中的节点,添加至 ArrayList 中,得到第一层的顺序遍历
          1         <-------当前层
         / \
        2    3
       /      \
      4        5
    
  3. 遍历完第一层后转换方向,变为从右往左遍历(isForward = false),

    1. 此时 stack2stackOutstack1stackIn,遍历完成后 stack2 为空,stack1 中存储着第三层的节点
    2. 遍历时不断 pop()stackOut 中的节点,添加至 ArrayList 中,得到第二层的逆序遍历
          1
         / \
        2    3      <-------当前层
       /      \
      4        5
    
  4. 以此类推

4、代码实现

代码

class Solution {
    public ArrayList<ArrayList<Integer>> zigzagLevelOrder(TreeNode root) {

        ArrayList<ArrayList<Integer>> zigzagOrder = new ArrayList<>();

        // Guard safe
        if (root == null) {
            return zigzagOrder;
        }

        // 双栈
        Deque<TreeNode> stack1 = new LinkedList<>();
        Deque<TreeNode> stack2 = new LinkedList<>();
        stack1.push(root); // 将根节点压入栈中
        boolean isForword = true; // 初始顺序为从左往右遍历

        // 但凡只要有一个栈中还有元素,则证明当前层还有节点
        while (stack1.isEmpty() == false || stack2.isEmpty() == false) {
            // 谁空谁来存储下一层节的点,谁不空谁存储了当前层节点
            Deque<TreeNode> stackOut = stack1.isEmpty() ? stack2 : stack1;
            Deque<TreeNode> stackIn = stack1.isEmpty() ? stack1 : stack2;
            zigzagOrder.add(new ArrayList<>());
            // 遍历完当前层的节点
            while (stackOut.isEmpty() == false) {
                // 栈顶节点出栈
                TreeNode curNode = stackOut.pop();
                // 添加至 ArrayList
                zigzagOrder.get(zigzagOrder.size() - 1).add(curNode.val);
                if (isForword) {
                    // 如果方向是从左往右,则先遍历当前节点的左节点
                    pushNode(stackIn, curNode.left);
                    pushNode(stackIn, curNode.right);
                } else {
                    // 如果方向是从右往左,则先遍历当前节点的右节点
                    pushNode(stackIn, curNode.right);
                    pushNode(stackIn, curNode.left);
                }
            }
            isForword = !isForword; // 改变方向
        }

        return zigzagOrder;
    }

    private void pushNode(Deque<TreeNode> stack, TreeNode node) {
        if (node != null) {
            stack.push(node);
        }
    }
}

class TreeNode {
    int val = 0;
    TreeNode left = null;
    TreeNode right = null;

    public TreeNode(int val) {
        this.val = val;
    }
}
  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值