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]. 挑战 你能使用非递归实现么? 解题: 通过递 ...
随机推荐
- 抓住那只牛!Catch That Cow POJ-3278 BFS
题目链接:Catch That Cow 题目大意 FJ丢了一头牛,FJ在数轴上位置为n的点,牛在数轴上位置为k的点.FJ一分钟能进行以下三种操作:前进一个单位,后退一个单位,或者传送到坐标为当前位置两 ...
- map转换成com.google.gson.JsonObject
String json =new Gson().toJson(map); JsonObject jsonObject =new JsonParser().parse(json).getAsJsonOb ...
- 解决MybatisGenerator多次运行mapper生成重复内容
MybatisGenerator插件是Mybatis官方提供的,这个插件存在一个固有的Bug,即当第一次生成了Mapper.xml之后,再次运行会导致Mapper.xml生成重复内容,而影响正常的运行 ...
- Spark 学习笔记之 优雅地关闭Spark Streaming
优雅地关闭Spark Streaming: 当touch stop文件后,程序仍然会再执行一次,执行完成后退出.
- MongoDB 学习笔记之 地理空间索引入门
地理空间索引: 地理空间索引,可用于处理基于地理位置的查询. Point:用于指定所在的具体位置,我们以restaurants为例: db.restaurants.insert({name: &quo ...
- mysql中间件分享(Mysql-prxoy,Atlas,DBProxy,Amoeba,cobar,TDDL)
hello 各位小伙伴大家好,我是小栈君,这期我们分享关于mysql中间件的研究,也就是数据层的读写分离和负载均衡,希望能够在实际的应用中能够帮助到各位小伙伴. 下期我们将继续分享go语言的系列讲解, ...
- 最简单的JS实现json转csv
工作久了,总会遇到各种各样的数据处理工作,比如同步数据,初始化一些数据,目前比较流行的交互数据格式就是JSON,可是服务器中得到的JSON数据如果提供给业务人员看的话可能会非常不方便,这时候,转成CS ...
- java中的String是不可变类
String s = "hello "; s += "world"; 这两行代码执行后,原始的String对象中的内容没有变 在这段代码中,s原先指向一个Str ...
- javascript进阶-《原型对象和原型链》
原创发布 by @一像素 2015.12 在Javascript中,万物皆对象,但对象也有区别,大致可以分为两类,即:普通对象Object 和 函数对象Function. 一般而言,通过new Fun ...
- BZOJ 4621: Tc605
Description 最初你有一个长度为 N 的数字序列 A.为了方便起见,序列 A 是一个排列. 你可以操作最多 K 次.每一次操作你可以先选定一个 A 的一个子串,然后将这个子串的数字全部变成原 ...