一、二叉树的按层打印与ZigZag打印

  1.按层打印:

      1                   Level 1 : 1
/ \          
2 3         Level 2 : 2 3 
/ / \         
4 5 6       Level 3 : 4 5 6
/ \
7 8    Level 4 : 7 8

  题目中要求同一层的节点必须打印在一行上,并且要求输出行号。这就需要我们在原来的广度优先遍历基础上,必须要搞明白的是什么时候要换行。

  解决方法:使用node类型的变量last便是正在打印的当前行的最右节点,nLast便是下一行的最右节点。假设每一层都做从左到右的宽度优先遍历,如果发现遍历的节点等于last,说明该换行了。换行之后令last=nLast,就可以继续下一行的打印过程,直到所有的节点都打印完。为了更新nLast,只需要让nLast一直跟踪记录宽度优先遍历队列中的最新加入的节点即可。这是因为最新加入队列的节点一定是目前已经发现的下一行的最右结点。所以在当前行打印完时,nLast一定是下一行所有节点中的最右节点。

  分析执行过程:

初始时,last=1,nLast=null,1入队列,此时:1 → 出的方向
1出队列,打印第一行,此时:→ ,2入队列,3入队列,nLast=3,此时:3 2 →,root == last,换行,last=3
2出队列,root=2,打印2,4入队列,nLast=4,此时 4 3 →
3出队列,root=3,打印3,5入队列,6入队列,nLast=6,此时6 5 4 → ,root==last,换行,last=6
...

  代码实现:

    public void printByLevel(Node root) {
if (root == null) return;
Queue<Node> queue = new LinkedList<>();
int level = 1;
Node last = root;
Node nLast = null;
queue.offer(root);
System.out.print("Level " + (level++) + " : ");
while (!queue.isEmpty()) {
root = queue.poll();
System.out.print(root.val + " ");
if (root.left != null) {
queue.offer(root.left);
nLast = root.left;
}
if (root.right != null) {
queue.offer(root.right);
nLast = root.right;
}
if (root == last && !queue.isEmpty()) {
System.out.print("\nLevel " + (level++) + " : ");
last = nLast;
}
}
System.out.println();
}

  2.ZigZag打印

      1                    Level 1 from left to right : 1
/ \          
2 3          Level 2 from right to left : 3 2
/ / \         
4 5 6        Level 3 from left to right :4 5 6
/ \
7 8    Level 4 from right to left : 8 7

  第一种思路:使用两个ArrayList结构list1和list2,用list1去收集当前层的节点,然后从左到右打印当前层,接着把当前层的孩子节点放进list2,并从右到左打印,接下来再把list2的所有节点的孩子节点放入list1,如此反复。这种思路不好的原因是因为:ArrayList结构为动态数组,在这个结构中,当元素数量到一定规模时将发生扩容操作,扩容操作的时间复杂度为O(N)是比较高的,这个结构增加和删除元素的时间复杂度也较高。总之,用这个结构对本地来讲数据结构不够纯粹和干净,所以最好不要使用。

  第二种思路:使用Java中的LinkedList结构,这个结构底层的实现就是非常纯粹的双端队列结构。这道题可以使用双端队列来解决。也就是(1)如果是从左到右的过程,那么一律从双端队列的头部弹出节点,如果弹出的节点没有孩子节点,当然不用放入任何节点到双端队列中;如果当前节点有孩子节点,先让左孩子从尾部进入双端队列,然后让右孩子从尾部进入双端队列。(2)如果是从右到左的过程,那么一律从双端队列的尾部弹出节点,如果弹出的节点没有孩子节点,当然不用放入任何节点到双端队列中;如果当前节点有孩子节点,先让右孩子从头部进入双端队列,然后再让左孩子从头部进入双端队列中。(3)如何确定切换(1)和(2)的时机,其实还是如何确定每一层最后一个节点的问题,原理就是,下一层最后打印的节点是当前层有孩子的节点的孩子节点中最先进入双端队列的节点。

  需要注意的问题:1.nLast = nLast == null ? root.left : nLast;是选择当前层的所有孩子中第一个进入deque的那个孩子,就是下一层最后打印的孩子,2.在打印完当前层后, 执行完last = nLast; 这条代码的后面,必须加上nLast = null;用来清空nLast,否则将输出不符合要求的内容。

    public void printByZigZag(Node root) {
if (root == null) return;
Deque<Node> deque = new LinkedList<>();
int level = 1;
boolean lr = true; // 一开始从左到右
Node last = root;
Node nLast = null;
deque.offerFirst(root);
printLevelAndOrientation(level++, lr);
while (!deque.isEmpty()) {
if (lr) { // 从左到右的过程
root = deque.pollFirst(); // 头部弹出节点
if (root.left != null) {
nLast = nLast == null ? root.left : nLast;
deque.offerLast(root.left); // 左孩子先从尾部进入
}
if (root.right != null) {
nLast = nLast == null ? root.right : nLast;
deque.offerLast(root.right); // 右孩子后从尾部进入
}
} else { // 从右到左的过程
root = deque.pollLast(); // 尾部弹出节点
if (root.right != null) {
nLast = nLast == null ? root.right : nLast;
deque.offerFirst(root.right); // 右孩子先从头部进入
}
if (root.left != null) {
nLast = nLast == null ? root.left : nLast;
deque.offerFirst(root.left); // 左孩子后从头部进入
}
}
System.out.print(root.val + " ");
if (root == last && !deque.isEmpty()) {
lr = !lr;
last = nLast;
nLast = null;
System.out.println();
printLevelAndOrientation(level++, lr);
}
}
} private void printLevelAndOrientation(int level, boolean lr) {
System.out.print("Level " + level + " from ");
System.out.print(lr ? "left to right : " : "right to left : ");
}

  分析执行过程:

初始时,last=1,nLast=null,1入队列,此时:(尾) 1  (头)
第一层,从左到右,nLast=2,2从尾部入队列,3从尾部入队列,此时:(尾) 3 2 (头),root=last,此时打印第一层,last=2,nLast=null
第二层,从右到左,3从尾部先出队列,root=3,nLast=6,6从头部入队列,5从头部入队列,此时:(尾)2 6 5(头)
第二层,从右到左,2从尾部先出队列,root=2,4从头部入队列,此时:(尾) 6 5 4 (头),root=last,此时第二层打印完毕,last=6,nLast=null
第三层,从左到右,4从头部出队列,root=4,此时:(尾)6 5 (头)
第三层,从左到右,5从头部出队列,root=5,nLast=7,7从尾部入队列,8从尾部入队列,此时:(尾)8 7 6 (头)
第三层,从左到右,6从头部出队列,root=6,此时:(尾)8 7 (头),root=last,第三层打印完毕,last=7,nLast=null
...

  

  二、

  

OptimalSolution(2)--二叉树问题(1)遍历与查找问题的更多相关文章

  1. 剑指offer面试题:输入某二叉树的前序遍历和中序遍历,输出后序遍历

    二叉树的先序,中序,后序如何遍历,不在此多说了.直接看题目描述吧(题目摘自九度oj剑指offer面试题6): 输入某二叉树的前序遍历和中序遍历的结果,请重建出该二叉树.假设输入的前序遍历和中序遍历的结 ...

  2. 算法学习笔记(六) 二叉树和图遍历—深搜 DFS 与广搜 BFS

    图的深搜与广搜 复习下二叉树.图的深搜与广搜. 从图的遍历说起.图的遍历方法有两种:深度优先遍历(Depth First Search), 广度优先遍历(Breadth First Search),其 ...

  3. LeetCode102. 二叉树的层次遍历

    102. 二叉树的层次遍历 描述 给定一个二叉树,返回其按层次遍历的节点值. (即逐层地,从左到右访问所有节点). 示例 例如,给定二叉树: [3,9,20,null,null,15,7], 3 / ...

  4. 五三想休息,今天还学习,图解二叉树的层序遍历BFS(广度优先)模板,附面试题题解

    壹 ❀ 引 我在从JS执行栈角度图解递归以及二叉树的前.中.后遍历的底层差异一文中,从一个最基本的数组遍历引出递归,在掌握递归的书写规则后,又从JS执行栈角度解释了二叉树三种深度优先(前序.中序后序) ...

  5. 二叉树的层序遍历 BFS

    二叉树的层序遍历,或者说是宽度优先便利,是经常考察的内容. 问题一:层序遍历二叉树并输出,直接输出结果即可,输出格式为一行. #include <iostream> #include &l ...

  6. codevs3143 二叉树的序遍历

    难度等级:白银 3143 二叉树的序遍历 题目描述 Description 求一棵二叉树的前序遍历,中序遍历和后序遍历 输入描述 Input Description 第一行一个整数n,表示这棵树的节点 ...

  7. codevs 3143 二叉树的序遍历

    传送门 Description 求一棵二叉树的前序遍历,中序遍历和后序遍历 Input 第一行一个整数n,表示这棵树的节点个数. 接下来n行每行2个整数L和R.第i行的两个整数Li和Ri代表编号为i的 ...

  8. lintcode : 二叉树的层次遍历II

    题目 二叉树的层次遍历 II 给出一棵二叉树,返回其节点值从底向上的层次序遍历(按从叶节点所在层到根节点所在的层遍历,然后逐层从左往右遍历) 样例 给出一棵二叉树 {3,9,20,#,#,15,7}, ...

  9. lintcode : 二叉树的层次遍历

    题目 二叉树的层次遍历 给出一棵二叉树,返回其节点值的层次遍历(逐层从左往右访问) 样例 给一棵二叉树 {3,9,20,#,#,15,7} : 3 / \ 9 20 / \ 15 7 返回他的分层遍历 ...

  10. lintcode :Binary Tree Preorder Traversal 二叉树的前序遍历

    题目: 二叉树的前序遍历 给出一棵二叉树,返回其节点值的前序遍历. 样例 给出一棵二叉树 {1,#,2,3}, 1 \ 2 / 3 返回 [1,2,3]. 挑战 你能使用非递归实现么? 解题: 通过递 ...

随机推荐

  1. 【翻译】Docker Compose 与Docker Stack的不同

    翻译原文 Docker引擎在1.12 版本集成了Docker Swarm,  带来了一些新工具. 现在可以无需安装 Docker Compose, 就可以利用docker-compose.yml 文件 ...

  2. Kafka系列一之架构介绍和安装

    Kafka架构介绍和安装 写在前面 还是那句话,当你学习一个新的东西之前,你总得知道这个东西是什么?这个东西可以用来做什么?然后你才会去学习它,使用它.简单来说,kafka既是一个消息队列,如今,它也 ...

  3. Python学习笔记整理总结【Django】Ajax

     一.Ajax(part1) Ajax即“Asynchronous Javascript And XML”(异步JavaScript和XML),是指一种创建交互式网页应用的网页开发技术,AJAX = ...

  4. 安装centos8

    一.     镜像下载 国内源下载镜像:(推荐) http://mirrors.aliyun.com/centos/8.0.1905/isos/x86_64/CentOS-8-x86_64-1905- ...

  5. 原创电子书《菜鸟程序员成长之路:从技术小白到阿里巴巴Java工程师》

    <菜鸟程序员成长之路:从技术小白到阿里巴巴Java工程师> 国庆节快乐!一年一度长度排第二的假期终于来了. 难得有十一长假,作者也想要休息几天啦. 不管你是选择出门玩,还是在公司加班,在学 ...

  6. jquery 全选,反选

    <?php foreach ($contents as $item) { ?> <tr> <td><input name="qx" val ...

  7. Java性能调优的9个实用方法

    1.使用StringBuilder StingBuilder 应该是在我们的Java代码中默认使用的,应该避免使用 + 操作符. 一般来说,使用 StringBuilder 的效果要优于使用 + 操作 ...

  8. html5一些特性

    html5可以理解为html+css+js 其目前可以解决:1.浏览器的兼容问题 2.统一web应用标准 3.解决文档结构定义不明确问题 4.解决web应用中的功能受限问题 5.是程序员编写的web应 ...

  9. mongodb完整安装

    在线下载安装依赖包 yum -y install gcc gcc-c++ yum -y install gcc gcc-c++ ncurses ncurses-devel cmake bison yu ...

  10. 《深入理解Java虚拟机》-----第12章 Java内存模型与线程

    概述 多任务处理在现代计算机操作系统中几乎已是一项必备的功能了.在许多情况下,让计算机同时去做几件事情,不仅是因为计算机的运算能力强大了,还有一个很重要的原因是计算机的运算速度与它的存储和通信子系统速 ...