题目叙述
请实现一个函数按照之字形打印二叉树,即第一行按照从左到右的顺序打印,第二层按照从右至左的顺序打印,第三行按照从左到右的顺序打印,其他行以此类推。
题目分析:
该题目和按层打印二叉树是属于同一个问题,只需要在按层打印时添加判断看是否是属于奇数行,如果是奇数行(行号从0开始)就先逆置再打印。
方法一:
public class Solution {
public ArrayList<ArrayList<Integer> > Print(TreeNode pRoot) {
ArrayList<ArrayList<Integer>> res = new ArrayList<ArrayList<Integer>>();
if(pRoot == null) return res;
Queue<TreeNode> q = new LinkedList<>();
q.offer(pRoot);
ArrayList<Integer> ans = new ArrayList<Integer>();
int start = 0, end = 1;
int count = 0;
while(!q.isEmpty()){
TreeNode root = q.remove();//或者是poll方法
ans.add(root.val);
start++;
if(root.left!=null) q.offer(root.left);
if(root.right!=null)q.offer(root.right);
if(start == end){
if(count%2!=0) Collections.reverse(ans);
res.add(ans);
start = 0;
end = q.size();
ans = new ArrayList<Integer>();
count++;
}
}
return res;
}
}
方法二:
方法一的效率在遇到海量数据时比较低,每行的逆序操作也是一个不小的开销,考虑在奇数行直接进行逆序ans。考虑使用Java的双端队列。
主要思想:
- 如果要是偶数行,就直接从头部弹出打印,弹出的时候如果有孩子节点,那么先让左孩子从尾部进队,右孩子再进队。
- 如果是奇数行,就从尾部弹出,然后判断当前节点是否有孩子节点,如果有孩子节点,就先让右孩子从头部进队,然后左孩子再从头部进队。
public class Solution {
public ArrayList<ArrayList<Integer> > Print(TreeNode pRoot) {
ArrayList<ArrayList<Integer>> res = new ArrayList<ArrayList<Integer>>();
if(pRoot == null) return res;
//声明定义双端队列
Deque<TreeNode> q = new LinkedList<>();
q.offer(pRoot);
ArrayList<Integer> ans = new ArrayList<Integer>();
int start = 0, end = 1;
int count = 0;
while(!q.isEmpty()){
if(count%2==0){
TreeNode root = q.pollFirst();
ans.add(root.val);
start++;
if(root.left!=null) q.offerLast(root.left);
if(root.right!=null)q.offerLast(root.right);
if(start == end){
//if(count%2!=0) Collections.reverse(ans);
res.add(ans);
start = 0;
end = q.size();
ans = new ArrayList<Integer>();
count++;
}
}else{
TreeNode root = q.pollLast();
ans.add(root.val);
start++;
if(root.right!=null)q.offerFirst(root.right);
if(root.left!=null) q.offerFirst(root.left);
if(start == end){
//if(count%2!=0) Collections.reverse(ans);
res.add(ans);
start = 0;
end = q.size();
ans = new ArrayList<Integer>();
count++;
}
}
}
return res;
}
}
思考:注意此处使用start和end以及已经当前队列的大小size()进确定打印的当前行的始末。由于有一个size()遍历大小的过程,海量数据还是效率不高,因此考虑逐步跟踪的方式确定打印行的始末。引入两个变量存储起始节点。
TreeNode nowlayer = pRoot;//表示正在打印层的最左(右)边的节点
TreeNode downlayer = null;//表示下一层的最右(左)边的节点
- 如果当前打印行是偶数行,则nowlayer存储下一行的最左边节点,打印完本行之后,将其赋值给downlayer。
- 如果当前打印行是奇数行,则nowlayer存储下一行的最右边节点,打印完本行之后,将其赋值给downlayer。
注意:求每一行的最左边和最右的时候,还要设置标志变量flag,用于标记是否已经找到了当前行的最左边和最右边节点并且赋值给了nowlayer。
import java.util.ArrayList;
import java.util.*;
import java.util.Collections;
/*
public class TreeNode {
int val = 0;
TreeNode left = null;
TreeNode right = null;
public TreeNode(int val) {
this.val = val;
}
}
*/
public class Solution {
public ArrayList<ArrayList<Integer> > Print(TreeNode pRoot) {
ArrayList<ArrayList<Integer>> res = new ArrayList<ArrayList<Integer>>();
if(pRoot == null) return res;
//声明双端队列
Deque<TreeNode> q = new LinkedList<>();
q.offer(pRoot);
ArrayList<Integer> ans = new ArrayList<Integer>();
int count = 0;
TreeNode nowlayer = pRoot;//表示正在打印层的最左(右)边的节点
TreeNode downlayer = null;//表示下一层的最右(左)边的节点
int flag = 0;
while(!q.isEmpty()){
if(count%2==0){
TreeNode root = q.pollFirst();
ans.add(root.val);
if(root.left!=null) {
q.offerLast(root.left);
if(flag == 0){
downlayer = root.left;
flag = 1;
}
}
if(root.right!=null){
q.offerLast(root.right);
if(flag == 0){
downlayer = root.right;
flag = 1;
}
}
if(root == nowlayer){
res.add(ans);
ans = new ArrayList<Integer>();
count++;
nowlayer = downlayer;
flag = 0;
}
}else{
TreeNode root = q.pollLast();
ans.add(root.val);
if(root.right!=null){
q.offerFirst(root.right);
if(flag == 0){
downlayer = root.right;
flag = 1;
}
}
if(root.left!=null){
q.offerFirst(root.left);
if(flag == 0){
downlayer = root.left;
flag = 1;
}
}
if(root == nowlayer){
res.add(ans);
ans = new ArrayList<Integer>();
count++;
nowlayer = downlayer;
flag = 0;
}
}
}
return res;
}
}
方法三:
使用两个栈进行求解
class Solution {
public List<List<Integer>> levelOrder(TreeNode root) {
List<List<Integer>> res = new ArrayList<>();
if(root==null) return res;
//声明两个栈
Stack<TreeNode> s1 = new Stack<>();
Stack<TreeNode> s2 = new Stack<>();
s1.push(root);
while(!s1.empty()||!s2.empty()){
List<Integer> ans = new ArrayList<>();
if(!s1.empty()){
while(!s1.empty()){
TreeNode top = s1.pop();
ans.add(top.val);
if(top.left!=null) s2.push(top.left);
if(top.right!=null) s2.push(top.right);
}
}else{
while(!s2.empty()){
TreeNode top = s2.pop();
ans.add(top.val);
if(top.right!=null) s1.push(top.right);
if(top.left!=null) s1.push(top.left);
}
}
res.add(ans);
}
return res;
}
}