welcome to my blog
剑指offer面试题牛客_二叉树_按之字形顺序打印二叉树(java版):
题目描述
请实现一个函数按照之字形打印二叉树,即第一行按照从左到右的顺序打印,第二层按照从右至左的顺序打印,第三行按照从左到右的顺序打印,其他行以此类推。
第四次做; 使用两个栈实现; 优秀案例如下
1
/ \
2 3
/ \
4 5
class Solution {
public List<List<Integer>> levelOrder(TreeNode root) {
List<List<Integer>> res = new ArrayList<>();
if(root==null)
return res;
//
Stack<TreeNode> stack1 = new Stack<>();
Stack<TreeNode> stack2 = new Stack<>();
stack1.add(root);
//true表示在偶数层, 根节点是第0层
boolean flag = true;
while(!stack1.isEmpty() || !stack2.isEmpty()){
res.add(new ArrayList<>());
if(flag==true){
while(!stack1.isEmpty()){
TreeNode cur = stack1.pop();
res.get(res.size()-1).add(cur.val);
if(cur.left!=null)
stack2.push(cur.left);
if(cur.right!=null)
stack2.push(cur.right);
}
flag=false;
}
else{
while(!stack2.isEmpty()){
TreeNode cur = stack2.pop();
res.get(res.size()-1).add(cur.val);
if(cur.right!=null)
stack1.push(cur.right);
if(cur.left!=null)
stack1.push(cur.left);
}
flag=true;
}
}
return res;
}
}
思路
- 使用两个栈
- 处理奇数层节点时, 先将其左孩子(如果有的话)压栈, 再将其右孩子(如果有的话)压栈
- 处理偶数层节点时, 先将其右孩子(如果有的话)压栈, 再将其左孩子(如果有的话)压栈
- 将左右孩子节点压栈的时候, 一定要先判断是否有左右孩子,
开始没有判断, 导致压入了null, 调用.val报错
第三次做, 感觉第二次的逻辑更严密一点点
import java.util.ArrayList;
import java.util.Stack;
public class Solution {
public ArrayList<ArrayList<Integer> > Print(TreeNode pRoot) {
ArrayList<ArrayList<Integer>> res = new ArrayList<>();
if(pRoot==null)
return res;
Stack<TreeNode> s1 = new Stack<>();
Stack<TreeNode> s2 = new Stack<>();
s1.push(pRoot);
TreeNode curr;
while(!s1.isEmpty() || !s2.isEmpty()){
res.add(new ArrayList<Integer>());
if(!s1.isEmpty()){
while(!s1.isEmpty()){
curr = s1.pop();
res.get(res.size()-1).add(curr.val);
if(curr.left!=null)
s2.push(curr.left);
if(curr.right!=null)
s2.push(curr.right);
}
}
else if(!s2.isEmpty()){
while(!s2.isEmpty()){
curr = s2.pop();
res.get(res.size()-1).add(curr.val);
if(curr.right!=null)
s1.push(curr.right);
if(curr.left!=null)
s1.push(curr.left);
}
}
}
return res;
}
}
第二次做, 使用两个栈实现
import java.util.ArrayList;
import java.util.Stack;
public class Solution {
public ArrayList<ArrayList<Integer> > Print(TreeNode pRoot) {
ArrayList<ArrayList<Integer>> res = new ArrayList<>();
if(pRoot==null)
return res;
Stack<TreeNode> s1 = new Stack<>();
Stack<TreeNode> s2 = new Stack<>();
s1.push(pRoot);
TreeNode curr;
while(!s1.isEmpty() || !s2.isEmpty()){
ArrayList<Integer> temp = new ArrayList<>();
if(!s1.isEmpty()){
while(!s1.isEmpty()){
curr = s1.pop();
temp.add(curr.val);
if(curr.left!=null)
s2.push(curr.left);
if(curr.right!=null)
s2.push(curr.right);
}
res.add(temp);
}
else{
while(!s2.isEmpty()){
curr = s2.pop();
temp.add(curr.val);
if(curr.right!=null)
s1.push(curr.right);
if(curr.left!=null)
s1.push(curr.left);
}
res.add(temp);
}
}
return res;
}
}
循环版
import java.util.ArrayList;
import java.util.Stack;
public class Solution {
public ArrayList<ArrayList<Integer> > Print(TreeNode pRoot) {
/*
思路: 使用两个栈实现,
处理奇数层的节点时,先将其左孩子压栈, 再将其右孩子压栈
处理偶数层的节点时,先将其右孩子压栈, 再将其左孩子压栈
*/
ArrayList<ArrayList<Integer>> alal = new ArrayList<ArrayList<Integer>>();
//input check
if(pRoot == null)
return alal;
//execute
Stack<TreeNode> s1 = new Stack<TreeNode>();
Stack<TreeNode> s2 = new Stack<TreeNode>();
s1.push(pRoot); //先将根节点压栈
TreeNode curr;
int flag=1;
while(true){
if(s1.isEmpty() && s2.isEmpty())
break; //遍历完毕, 调出循环
if(flag==1){ //当前处理奇数层的节点
alal.add(new ArrayList<Integer>()); //为当前层添加集合
while(!s1.isEmpty()){
curr = s1.pop(); //出栈
alal.get(alal.size()-1).add(curr.val); // 打印
if(curr.left != null)
s2.push(curr.left); //如果有左孩子的话, 左孩子入栈
if(curr.right != null)
s2.push(curr.right); //如果有右孩子的话,右孩子入栈
}
flag = 1 - flag;
}
else{
alal.add(new ArrayList<Integer>());
while(!s2.isEmpty()){
curr = s2.pop();
alal.get(alal.size()-1).add(curr.val);
if(curr.right != null)
s1.push(curr.right); //如果有右孩子的话, 右孩子入栈
if(curr.left != null)
s1.push(curr.left); //如果有左孩子的话, 左孩子入栈
}
flag = 1 - flag;
}
}
return alal;
}
}
递归版, 不如循环版直观易懂
- 处理完当前层所有的节点后, 才能对下一层的节点进行递归调用
- 比较难理解的是递归函数中的
Core(tn.left, layer+1, s1, s2, alal); Core(tn.right, layer+1, s1, s2, alal);
这两句的顺序不影响结果. 其实不难理解, 处理完某个奇数层之后, 通过Core处理接下来的偶数层节点, 由于在处理奇数层节点时,把当前偶数层节点的节点排好序放在集合里了,所以Core不会影响当前层的打印顺序,Core做的是处理已经安排好的栈s2, 有了安排好的栈s2, Core就能根据s2中的顺序安排好s1. - 总之, 递归函数中的形参TreeNode p, 只是用来判断p是否为空的, 不会对处理顺序产生影响
- 处理顺序全靠两个栈,以及压栈顺序决定
import java.util.ArrayList;
import java.util.Stack;
/*
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>> alal = new ArrayList<ArrayList<Integer>>();
//input check
if(pRoot==null)
return alal;
//execute
Stack<TreeNode> s1 = new Stack<TreeNode>();
Stack<TreeNode> s2 = new Stack<TreeNode>();
s1.push(pRoot);
Core(pRoot, 1, s1, s2, alal);
return alal;
}
// 递归函数逻辑: 根据当前的层数, 将对应栈中的节点弹出, 并将节点对应的值加入对应的集合中, 再递归处理左右孩子
public void Core(TreeNode p, int layer, Stack<TreeNode> s1, Stack<TreeNode> s2, ArrayList<ArrayList<Integer>> alal){
if(p == null) return;
TreeNode curr;
if(layer%2==1){ //当前在奇数层
if(alal.size() < layer)
alal.add(new ArrayList<Integer>());
ArrayList<TreeNode> al = new ArrayList<TreeNode>();
while(!s1.isEmpty()){
curr = s1.pop();
al.add(curr);
alal.get(layer-1).add(curr.val);
if(curr.left != null)
s2.push(curr.left);
if(curr.right != null)
s2.push(curr.right);
}
for(TreeNode tn:al){//处理完当前层所有的节点后, 才能对下一层的节点进行递归调用
Core(tn.left, layer+1, s1, s2, alal);
Core(tn.right, layer+1, s1, s2, alal);
}
}
else{ //当前在偶数层
if(alal.size() < layer)
alal.add(new ArrayList<Integer>());
ArrayList<TreeNode> al = new ArrayList<TreeNode>();
while(!s2.isEmpty()){
curr = s2.pop();
al.add(curr);
alal.get(layer-1).add(curr.val);
if(curr.right != null)
s1.push(curr.right);
if(curr.left != null)
s1.push(curr.left);
}
for(TreeNode tn:al){//处理完当前层所有的节点后, 才能对下一层的节点进行递归调用
Core(tn.left, layer+1, s1, s2, alal);
Core(tn.right, layer+1, s1, s2, alal);
}
}
}
}