题目:
(一)从上往下打印出二叉树的每个结点,同一层的结点按照从左到右的顺序打印。
(二)从上到下按层打印二叉树,同一层的结点按从左到右的顺序打印,每一层打印到一行。
(三)请实现一个函数按照之字形顺序打印二叉树,即第一行按照从左到右的顺序打印,第二层按照从右到左的顺序打印,第三行再按照从左到右的顺序打印,其他行以此类推。
考点:举例让抽象问题具体化,树的遍历
思路:
(一)不分行从上往下打印二叉树:该题即为对二叉树的层序遍历,使用宽度优先遍历,结点满足先进先出的原则,采用队列。每从队列中取出头部结点并打印,若其有子结点,把子结点放入队列尾部,直到所有结点打印完毕。
实现:
public ArrayList<Integer> PrintFromTopToBottom(TreeNode root) {
ArrayList<Integer> res = new ArrayList<Integer>();
if (root == null){
return res;
}
Queue<TreeNode> q = new LinkedList<>();
q.offer(root);
while(!q.isEmpty()){
TreeNode t = q.poll();
res.add(t.val);
if(t.left!=null){
q.offer(t.left);
}
if(t.right!=null){
q.offer(t.right);
}
}
return res;
}
(二)分行从上到下打印二叉树:分行打印,要控制每次poll的数量,在第一题基础上增加两个变量:当前层结点数目pCount,下一层结点数目nextCount。根据当前层结点数目来打印当前层结点,同时计算下一层结点数目,之后令pCount等于nextCount,nextCount=0,重复循环,直到打印完毕。
public ArrayList<ArrayList<Integer>> Print(TreeNode pRoot) {
ArrayList<ArrayList<Integer>> res = new ArrayList<>();
if (pRoot == null) return res;
Queue<TreeNode> queue = new LinkedList<>();
queue.offer(pRoot);
int pCount = 0; //当前层结点数目
int nextCount = 1; //下一层结点数目
TreeNode node = null;
while(!queue.isEmpty()){
pCount = nextCount;
nextCount = 0;
ArrayList<Integer> tmp = new ArrayList<>();
for (int i = 1; i <= pCount; i++) {
node = queue.poll();
tmp.add(node.val);
if (node.left != null) {
queue.offer(node.left);
nextCount++;
}
if (node.right != null) {
queue.offer(node.right);
nextCount++;
}
}
res.add(tmp);
}
return res;
}
(三)之字形打印二叉树:
(1)自己开始想的方法:在(二)的基础上,多定义一个表示当前层数的变量level。每层结点不直接打印,放入一个数组中,根据此时的层数level的奇偶来决定正向还是反向打印数组。
public ArrayList<ArrayList<Integer>> Print(TreeNode pRoot) {
ArrayList<ArrayList<Integer>> res = new ArrayList<>();
if(pRoot == null) return res;
int pCount = 0;
int nextCount = 1;
int hang = 0;
Queue<TreeNode> queue = new LinkedList<>();
queue.offer(pRoot);
while(!queue.isEmpty()){
pCount = nextCount;
nextCount = 0;
hang ++;
ArrayList<Integer> tmp = new ArrayList<>();
int index = 0;
int[] cunchu = new int[pCount];
for(int i=1;i<=pCount;i++){
TreeNode t = queue.poll();
cunchu[index++] = t.val;
if(t.left!=null){
nextCount++;
queue.offer(t.left);
}
if(t.right!=null){
nextCount++;
queue.offer(t.right);
}
}
if(hang%2!=0){
for(int i=0;i<cunchu.length;i++){
tmp.add(cunchu[i]);
}
}else{
for(int i=cunchu.length-1;i>=0;i--) {
tmp.add(cunchu[i]);
}
}
res.add(tmp);
}
return res;
}
(2)书中提供的方法:采用两个栈,对于不同层的结点,一个栈用于正向存储,一个栈用于逆向存储,打印出来就正好是相反方向。
public ArrayList<ArrayList<Integer>> Print(TreeNode pRoot) {
ArrayList<ArrayList<Integer>> arrayList = new ArrayList<>();
if(pRoot == null) return arrayList;
Stack<TreeNode> stack1 = new Stack<>();
Stack<TreeNode> stack2 = new Stack<>();
stack1.push(pRoot);
int hang = 0 ;
int pCount = 0;
int nextCount =1;
TreeNode node = null;
while(stack1.empty()==false || stack2.empty()==false){
hang ++ ;
pCount = nextCount;
nextCount = 0;
ArrayList<Integer> tmp = new ArrayList<>();
if(hang%2 == 1){
for(int i=1;i<=pCount;i++){
node=stack1.pop();
if(node.left!=null){
stack2.push(node.left);
nextCount++;
}
if(node.right!=null){
stack2.push(node.right);
nextCount++;
}
tmp.add(node.val);
}
}else{
for(int i=1;i<=pCount;i++){
node=stack2.pop();
if(node.right!=null){
stack1.push(node.right);
nextCount++;
}
if(node.left!=null){
stack1.push(node.left);
nextCount++;
}
tmp.add(node.val);
}
}
arrayList.add(tmp);
}
return arrayList;
}
收获:
- 层序遍历时,一般都要用到队列
- 队列可以用LinkedList类(LinkedList类实现了Queue接口,因此我们可以把LinkedList当成Queue来用),方法:poll() 和 offer(Obj) 。
- 在分行打印时,定义的两个int有明显实际意义(当前层结点数目,下一层结点数目)。自己编程时,一开始只知道要设置两个变量,但没有去想这两个变量的实际意义。当明白变量意义时,自己的思路会更清晰,而且代码可读性也更好。