二叉树17:按之字形顺序打印二叉树

来源:互联网 发布:为知笔记活动 编辑:程序博客网 时间:2024/06/05 17:01

题目:请实现一个函数按照之字形打印二叉树,即第一行按照从左到右的顺序打印,第二层按照从右至左的顺序打印,第三行按照从左到右的顺序打印,其他行以此类推。

思路:方法一,思路很简单但是毫无技巧性可言。还是按照按层遍历的方式设置一个队列queue,分层的指针last,nlast将二叉树从上到下,从左到右进行遍历,每一层放入到list中,左后对得到的list<list>进行遍历,将list间隔地从左到右和从右到左放入到新的list中(即对偶数个list从后万千遍历放入到新的list中),然后返回结果list即可。这样做是对结果进行了重新处理,可行,但是没哟充分利用“之字形”的特点来解决问题。

方法二:很巧妙,需要充分理解之字形遍历的特点。可以使用2个栈来解决问题,2个栈交替工作可以做到之字形输出。创建2个栈stack1和stack2,先将root结点压入到stack1中,然后开始循环过程:从stack1中弹出root,每次弹出一个结点时要判断它的左右子节点是否是null,如果不为null就压入到另一个栈stack2中,即这里弹出结点的栈和压入子节点的栈应该是分开的2个栈,从stack1中弹出的结点它的子节点要压入到另一个栈stack2中,当stack1中的结点弹光后,先判断stack2里面的元素是否为空,如果stack2中的结点数目也为0,那么说明在stack1弹出结点的时候没有再压入子节点到stack2中,说明此时已经没有下一层了,于是遍历结束,即遍历循环结束的判断条件是:stack1.isEmpty()并且stack2.isEmpty()。Stack1弹完后这一层就遍历结束了,它的下一层的结点都在stack2中了,为了便于循环中的操作,将stack2赋值给stack1,将stack2重新创建,这样就始终只要对stack1进行弹出,对stack2进行压入操作了,在循环代码中的实现较为简单。即基本操作过程就是从stack1中弹出结点,并将结点的子节点放入到另一个栈stack2中,当stack1遍历结束后就将stack2赋值给stack1,并将stack2重建,直到stack1和stack2都变空为止。需要注意的是:对于每一层,要求打印的顺序是不同的,对于奇数层,要求从左到右遍历结点,对于偶数层要求从右到左遍历结点,如何实现?对于偶数层,例如②③,在弹出时是先弹出③再弹出②,此时为了使得它的下一层能够从左到右输出,应该使得对于弹出的结点,先将其右孩子放入栈stack2,在将左孩子放入stack2,于是stack2中先后压入⑦⑥⑤④,弹出时的顺序就是④⑤⑥⑦;同理对于奇数层的结点,例如第三层,在遍历时的顺序是④⑤⑥⑦,它的下一层要求遍历顺序是15,14,13,12,11,⑩⑨⑧,于是在对④⑤⑥⑦的子节点压入栈是应该先压入左孩子再压入右孩子,于是先后向stack2中压入⑧⑨⑩11,12,13,14,15.总结来说,对于奇数层的结点在弹出时,它的孩子压入stack2的顺序应该是从左到右,即先压入左孩子再压入右孩子;对于偶数层的结点在弹出时,它的孩子压入stack2的顺序应该是从右到左,即先压入右孩子再压入左孩子。这种交替需要在循环体中写2中遍历子孩子的代码,并且记录一个层数计数器levelCount,通对其的奇偶性判断来决定使用哪一段代码即可。

这里注意,使用2个栈进行层遍历后可以自动对层进行识别,当stack1遍历完后就表示一层的遍历结束了,于是并不需要手动的设置和调整指针last和nlast来识别分层。

 

import java.util.*;//之字形遍历:使用2个栈,遍历先压入左结点再压入右结点和先右结点再左结点交替进行;自动换行,不用last和nlastpublic class Solution {    //创建结果集,成员变量    ArrayList<ArrayList<Integer>> results;    public ArrayList<ArrayList<Integer>> Print(TreeNode pRoot) {        results=new ArrayList<ArrayList<Integer>>();        //特殊输入        if(pRoot==null) return results;        //调用方法解决问题        this.process(pRoot);        //返回结果集        return results;    }        //该方法用来之字形遍历二叉树,并将每层结果放入到results中    private void process(TreeNode root){        //①创建辅助栈        Stack<TreeNode> stack1=new Stack<>();        Stack<TreeNode> stack2=new Stack<>();        //每一层的结果集        ArrayList<Integer> list=new ArrayList<>();        //第一变量记录层数        int levelCount=1;        //②先将root压入栈        stack1.push(root);        //③循环:弹栈遍历--左右孩子入栈--交换stack1和stack2        //只要stack1和stack2不全为空就可以继续        while(!(stack1.isEmpty()&&stack2.isEmpty())){            //弹出并遍历            TreeNode temp=stack1.pop();            list.add(temp.val);            //左右孩子压入stack2中            if(levelCount%2!=0){                //奇数层,先左再右                if(temp.left!=null){                    stack2.push(temp.left);                }                if(temp.right!=null){                    stack2.push(temp.right);                }            }else{                //偶数层,先右再左                if(temp.right!=null){                    stack2.push(temp.right);                }                if(temp.left!=null){                    stack2.push(temp.left);                }            }                        //判断此时stack1是否为空,即本层是否遍历完成            if(stack1.isEmpty()){                //本层遍历完成:换list,换stack1和stack2;层数加1                results.add(list);                list=new ArrayList<Integer>();                stack1=stack2;                stack2=new Stack<TreeNode>();                //千万记得层数+1                levelCount++;            }        }    }}

几个常识:

队列LinkedList中添加使用add(),弹出使用poll();

栈Stack中入栈用push(),出栈用pop();  记:观众站着听流行音乐

遍历时结果集合中通常存放的是Integer而不是TreeNode,因此是ArrayList<Integer>

取余运算符是%,而/是取整的运算符,不要弄错,没有mod。


0 0
原创粉丝点击