import java.io.BufferedReader; import java.io.InputStreamReader; import java.util.Queue; import java.util.Deque; import java.util.LinkedList; public class Main { /** * 测试用例:( 注意官方给的输入数据有问题,更正后如下) * 8 1 * 1 2 3 * 2 4 0 * 4 0 0 * 3 5 6 * 5 7 8 * 7 0 0 * 8 0 0 * 6 0 0 */ public static void main(String[] args) throws Exception { BufferedReader in = new BufferedReader(new InputStreamReader(System.in)); String[] firstRow = in.readLine().split(" "); int row = Integer.parseInt(firstRow[0]); TreeNode<String> root = TreeUtils.makeBinaryTreeByInput(in); //按层打印 PrintByLevel.printByLevel2(root); System.out.println(); //之字形打印 PrintByZigZag.printByZigZag(root); } } /** * @描述: 按Z字形按层打印二叉树的节点 * @思路:只使用一个双端队列dq * 定义规则: * 规则1:若顺序是从左到右,从dq的头部弹出节点node;若node有孩子,以“先左后右”的形式从尾部进入dq. * 规则2:若顺序是从右到左,从dq的尾部弹出节点node;若node有孩子,以“先右后左”的形式从头部进入dq. * 确定规则1和规则2转换时机:(实质是一个确定每一层最后一个节点的问题) * 经总结发现:**下一层最后打印的节点 == 当前层有孩子节点中最先进入dq的节点 ** * @复杂度: * @链接: */ class PrintByZigZag { public static <T> void printByZigZag(TreeNode<T> root) { Deque<TreeNode<T>> queue = new LinkedList<>(); boolean isLeft = true; TreeNode last = root; TreeNode nlast = null; int level = 1; queue.add(root); pringLevelAndOrientation(level++, isLeft); while (!queue.isEmpty()) { TreeNode curr = null; if (isLeft) { curr = queue.pollFirst(); if (curr.left != null) { queue.addLast(curr.left); nlast = nlast == null ? curr.left : nlast; } if (curr.right != null) { queue.addLast(curr.right); nlast = nlast == null ? curr.right : nlast; } } else { curr = queue.pollLast(); if (curr.right != null) { queue.addFirst(curr.right); nlast = nlast == null ? curr.right : nlast; } if (curr.left != null) { queue.addFirst(curr.left); nlast = nlast == null ? curr.left : nlast; } } System.out.print(curr.value + " "); //换行时刻(注:其中!queue.isEmpty()判断只是为了打印好看,可忽略) if (curr == last && !queue.isEmpty()) { last = nlast; nlast = null; isLeft = !isLeft; System.out.println(); pringLevelAndOrientation(level++, isLeft); } } } //只是为了打印好看 public static void pringLevelAndOrientation(int level, boolean lr) { System.out.print("Level " + level + " from "); System.out.print(lr ? "left to right: " : "right to left: "); } } /** * @描述:按层遍历二叉树 * @思路:进行宽度优先遍历,使用队列queue存储当前层和下一层的节点信息; 即: 从queue的头部弹出节点node;若node有孩子,以“先左后右”的形式从尾部进入queue. * @复杂度:时间O(N) 空间O(1) * @链接:https://www.nowcoder.com/practice/6a1815a85bfc411d9295bc017e6b6dbe */ class PrintByLevel { /* * 宽度优先遍历进阶版 - 按层换行打印 * 如何确定换行? * 记录下"当前行的最右节点last" 和 "下一行的最右节点nlast" * 换行时刻:当前节点==last,然后更新last: last = nlast * * 结果形式: * Level 1:1 * Level 2:2 3 * Level 3:4 5 */ public static void printByLevel2(TreeNode root) { Queue<TreeNode> queue = new LinkedList<>(); int level = 1; TreeNode last = root; //当前行的最右节点 TreeNode nlast = null; //下一行的最右节点 queue.offer(root); System.out.print("Level " + level++ + " : "); while (!queue.isEmpty()) { TreeNode curr = queue.poll(); // -- 核心点 System.out.print(curr.value + " "); if (curr.left != null) { queue.offer(curr.left); nlast = curr.left; } if (curr.right != null) { queue.offer(curr.right); nlast = curr.right; } //换行时刻(注:其中!queue.isEmpty()判断只是为了打印好看,可忽略) if (curr == last && !queue.isEmpty()) { last = nlast; System.out.print("\nLevel " + level++ + " : "); } } } /* * 宽度优先遍历进阶版 - 按层换行打印 * 如何确定换行? * 标识出 当前层和下一层的分界位置 * 将队列中的值按层一次一次遍历;即:一次性将当前队列中的当前层的元素遍历完,再在一次性遍历下一层队列元素; 重点在于标识当前层和下一层的分界位置; */ public static void printByLevel3(TreeNode root) { Queue<TreeNode> queue = new LinkedList<>(); int level = 1; queue.offer(root); System.out.print("Level " + level++ + ":"); while (!queue.isEmpty()) { int length = queue.size(); // - 核心点 区分:标识当前层和下一层的分界位置 //换行时刻 for (int i = 0; i < length; i++) { TreeNode curr = queue.poll(); if (curr.left != null) { queue.offer(curr.left); } if (curr.right != null) { queue.offer(curr.right); } System.out.print(curr.value + " "); } //注:其中!queue.isEmpty()判断可忽略,这里只是为了打印好看,才加的判断 if (!queue.isEmpty()) { System.out.print("\nLevel " + level++ + " : "); } } } } class TreeUtils { /** * 通过控制台输入的方式,采用递归的方式创建一颗二叉树 */ public static TreeNode<String> makeBinaryTreeByInput(BufferedReader in) throws Exception { String[] nodeVals = in.readLine().split(" "); //数组中第一个数是根节点 TreeNode<String> node = new TreeNode<>(nodeVals[0]); //通过递归确定了层数 if (!"0".equals(nodeVals[1])) { node.left = makeBinaryTreeByInput(in); } if (!"0".equals(nodeVals[2])) { node.right = makeBinaryTreeByInput(in); } return node; } } class TreeNode<T> { public T value; public TreeNode<T> left; public TreeNode<T> right; public TreeNode(T value) { this.value = value; } public TreeNode() { } public TreeNode(TreeNode left, T value, TreeNode right) { this.left = left; this.value = value; this.right = right; } public static int getHeight(TreeNode root) { if (root == null) { return 0; } int i = getHeight(root.left); int j = getHeight(root.right); return (i < j) ? j + 1 : i + 1; } public static int getSize(TreeNode root) { if (root == null) { return 0; } return 1 + getSize(root.left) + getSize(root.right); } }