OptimalSolution(2)--二叉树问题(1)遍历与查找问题
一、二叉树的按层打印与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)遍历与查找问题的更多相关文章
- 剑指offer面试题:输入某二叉树的前序遍历和中序遍历,输出后序遍历
二叉树的先序,中序,后序如何遍历,不在此多说了.直接看题目描述吧(题目摘自九度oj剑指offer面试题6): 输入某二叉树的前序遍历和中序遍历的结果,请重建出该二叉树.假设输入的前序遍历和中序遍历的结 ...
- 算法学习笔记(六) 二叉树和图遍历—深搜 DFS 与广搜 BFS
图的深搜与广搜 复习下二叉树.图的深搜与广搜. 从图的遍历说起.图的遍历方法有两种:深度优先遍历(Depth First Search), 广度优先遍历(Breadth First Search),其 ...
- LeetCode102. 二叉树的层次遍历
102. 二叉树的层次遍历 描述 给定一个二叉树,返回其按层次遍历的节点值. (即逐层地,从左到右访问所有节点). 示例 例如,给定二叉树: [3,9,20,null,null,15,7], 3 / ...
- 五三想休息,今天还学习,图解二叉树的层序遍历BFS(广度优先)模板,附面试题题解
壹 ❀ 引 我在从JS执行栈角度图解递归以及二叉树的前.中.后遍历的底层差异一文中,从一个最基本的数组遍历引出递归,在掌握递归的书写规则后,又从JS执行栈角度解释了二叉树三种深度优先(前序.中序后序) ...
- 二叉树的层序遍历 BFS
二叉树的层序遍历,或者说是宽度优先便利,是经常考察的内容. 问题一:层序遍历二叉树并输出,直接输出结果即可,输出格式为一行. #include <iostream> #include &l ...
- codevs3143 二叉树的序遍历
难度等级:白银 3143 二叉树的序遍历 题目描述 Description 求一棵二叉树的前序遍历,中序遍历和后序遍历 输入描述 Input Description 第一行一个整数n,表示这棵树的节点 ...
- codevs 3143 二叉树的序遍历
传送门 Description 求一棵二叉树的前序遍历,中序遍历和后序遍历 Input 第一行一个整数n,表示这棵树的节点个数. 接下来n行每行2个整数L和R.第i行的两个整数Li和Ri代表编号为i的 ...
- lintcode : 二叉树的层次遍历II
题目 二叉树的层次遍历 II 给出一棵二叉树,返回其节点值从底向上的层次序遍历(按从叶节点所在层到根节点所在的层遍历,然后逐层从左往右遍历) 样例 给出一棵二叉树 {3,9,20,#,#,15,7}, ...
- lintcode : 二叉树的层次遍历
题目 二叉树的层次遍历 给出一棵二叉树,返回其节点值的层次遍历(逐层从左往右访问) 样例 给一棵二叉树 {3,9,20,#,#,15,7} : 3 / \ 9 20 / \ 15 7 返回他的分层遍历 ...
- lintcode :Binary Tree Preorder Traversal 二叉树的前序遍历
题目: 二叉树的前序遍历 给出一棵二叉树,返回其节点值的前序遍历. 样例 给出一棵二叉树 {1,#,2,3}, 1 \ 2 / 3 返回 [1,2,3]. 挑战 你能使用非递归实现么? 解题: 通过递 ...
随机推荐
- Github 入门1 (下载git , 连接本地库与github仓库)
/* 本篇建立在以注册GitHub账号的前提下*/ (1) 下载 git https://www.git-scm.com // win10 可以直接红色箭头标识的 Download 2.22.0 ...
- 基于Docker搭建大数据集群(四)Spark部署
主要内容 spark部署 前提 zookeeper正常使用 JAVA_HOME环境变量 HADOOP_HOME环境变量 安装包 微云下载 | tar包目录下 Spark2.4.4 一.环境准备 上传到 ...
- Spring MVC-从零开始-@ResponseBody(未完待续)
Spring MVC-从零开始-@ResponseBody(未完待续)
- .net core 3.0 Signalr - 02 使用强类型的Hub
## 强类型的优缺点 - 优点 强类型的Hub可以避免魔法函数名,相比弱类型更容易维护和发现问题,直接上代码 - 缺点 特么的得多些好几行代码 ## 代码 ### 接口定义 ``` C# /// // ...
- ThinkPHP5实现定时任务
ThinkPHP5实现定时任务 最近使用ThinkPHP5做了个项目,项目中需要定时任务的功能,感觉有必要分享下 TP5做定时任务使用到command.php的 步骤如下: 1.配置command.p ...
- Docker详解(四) — Dockerfile剖析
目录 1.Dockfile简介 2. Dockerfile构建过程解析 3. Dockerfile体系结构 4. 案例 4.1 自定义mycentos 4.2 CMD/ENTRYPOINT 镜像案例 ...
- Kotlin编译时注解,简单实现ButterKnife
ButterKnife在之前的Android开发中还是比较热门的工具,帮助Android开发者减少代码编写,而且看起来更加的舒适,于是简单实现一下ButterKnife,相信把下面的代码都搞懂,看Bu ...
- 服务网关Spring Cloud Zuul
Spring Cloud Zuul 开发环境 idea 2019.1.2 jdk1.8.0_201 Spring Boot 2.1.9.RELEASE Spring Cloud Greenwich S ...
- 原生js动态创建文本内容的几种方式
1.通过CreateTextNode文本节点 首先创建该元素(元素节点),然后向一个已存在的元素追加该文本节点 <!DOCTYPE html> <html> <body& ...
- 洛谷 P3833 [SHOI2012]魔法树
题目背景 SHOI2012 D2T3 题目描述 Harry Potter 新学了一种魔法:可以让改变树上的果子个数.满心欢喜的他找到了一个巨大的果树,来试验他的新法术. 这棵果树共有N个节点,其中节点 ...