在网上看二叉树的遍历时,发现很多作者前序和中序与后序的处理方法完全不一样。经过自己的思考,发现这三种方法起使可以用同一种思路解决。如果不了解二叉树,可以先百度二叉树的原理。
先拿后序遍历举一个例子,二叉树如下所示:
后序会按照“左右中”的顺序来遍历整个二叉树。在首次遍历中,会将结点“1”和它所有子孙节点形成的“1子树”作为左结点,将“2子树”作为右节点结点,不执行输出。然后进入“1子树”,以结点1为中间结点,将“3子树”作为左结点,“4子树”作为右结点,不执行输出。最后,由于7结点不满足成立“7子树”的条件--由结点节点,被输出了,同理,8结点也被输出了。后面的过程请自己推演一边,方便下面的理解。
在上面的步骤中,其实所有结点都经过一个同样的步骤:检测自己是否有子结点,有就按照“左右中”的顺序继续遍历,否则输出自己。对于0,1,2,3,4等有子结点的结点来说,会经历检测出有子结点和检测出没有子结点两个步骤。怎么会没有子结点呢,这是我假设已经输出的结点“脱离”了子树,那么它们就没有子结点了。按照这个思路再次看遍历过程:
结点0:有子结点,继续遍历过程,将结点0,2存放起来,进入结点1。
结点1:有子结点,继续遍历过程,将结点1,4存放起来,进入结点3。
结点3:有子结点,继续遍历过程,将结点3,8存放起来,进入结点7。
结点7:没子结点,输出自己。
那么后面就是处理存放结点的过程了。这里使用栈来存放结点,存放的顺序是中左右,即后序遍历的反序,弹出来即后序。目前栈中存放结点的顺序(从栈底到栈顶):8,3,4,2,0.
弹出栈顶是结点8,正是右节点。只需要继续执行遍历过程即可。所以,结点7的完整步骤如下:
结点7:没子结点,输出自己。从栈中弹出一个结点,继续遍历过程。
新的问题来了,过程执行到结点4的时候,不可能直接将结点4输出了啊,结点9没参与过遍历,这就涉及到“脱离”了。当我们继续推演遍历过程中,发现一个特点,所有具有子结点的结点,都使用了两次,第一次是检测是否有子结点,自己不输出,第二次是输出自己。我们不如把输出自己当作新的检测导致的结果。新的检测是该结点是否检测过自己具有子结点。如果结点没检测过自己的子结点,那么检测,否则输出自己。这涉及到一个新栈--检测栈。
再次看遍历过程
结点0:弹出自己(在方法执行之前将根结点和没检测过首先压入栈),没检测且有子结点,继续遍历过程,将结点0,2,1存放和它们的检测情况起来。
在存放结点0,2,1的同时,将这三个结点是否检测过是否具有子结点的结果放入检测栈中。
栈中情况:
栈顶
1--false
2--false
0--true
结点1:弹出结点1,没检测且有子结点,继续遍历过程,将结点1,4,3和它们的检测情况存放起来。
栈顶
3--false
4--false
1--true
2--false
0--true
结点7:
栈顶
7--false
8--false
3--true
4--false
1--true
2--false
0--true.
到了结点7时,弹,进行检测,发现没有子结点(不需要存放自己和自己的检测情况了)输出自己。然后弹出结点8,记住,弹出栈中的内容时要同时弹出检测栈中的内容。
结点8与结点7的过程相同。到了结点3,发现已经检测过,输出自己。
到了结点4,相信你应该直到怎么办了吧。
代码如下:
下面再来中序的代码:
可以看到,代码几乎一样,只是在每次结点处理中对子结点和自己的顺序调转了一下而已,这是因为中序遍历也可以使用上面的思路解决。相信你们也可以自己编写前序遍历的代码了吧。有缘再见。