二叉树的前序遍历,中序遍历和后序遍历:
递归法:
递归法非常直观好理解,首先我们要知道什么是前序遍历。前序遍历是指每次先记录当前节点,再记录左边子节点,最后记录右边子节点。如果是个只有三个节点的二叉树,相信所有人都会做。最简单的方法就是首先记录root.val,然后记录root.left.val,最后记录root.right.val。那么对于超过三个的树,难点在于left和right不是一个节点而是一个子树。而这也是递归的好用之处。因为我们知道,对于前序遍历,整个树和任何一个子树都遵循同一规律,那么子树就可以递归。这样就好写了很多,首先我们判断终止条件,如果一个节点已经为None了,就没有看下去的意义了,那么这就是终止条件。确定终止条件后,我们开始准备递归规律。正如上文所言,我们先记录当前父节点,再记录子节点,那么我们就先记录下当前节点值。然后把左子树当成左节点递归,右边一样,就可以很容易得到结果。题解如下:
事实上这个题解还是容易晕,不知道helper每次都在干什么。我们只需要知道,只有在None的时候和helper会停下。其它时候helper作用就是记录当前点的value,而记录的是哪个点,是一个栈的顺序,进行完里面层才进行前面层,但是由于我们记录result在递归前面,其实就是从第一层开始记录,一点点往里面走。是一种自顶向下的感觉。
看完了前序遍历,那么中序和后序就简单多了。它们和前序的唯一区别在于遍历的顺序不同。此时我们想法也一样,就是想最简单情况,比如只有三个节点,该怎么做。对于中序遍历,如果只有三个,我们只需要先记录root.left.val,再记录root.val,最后记录root.right.val。那么对于多于三个的树,也是一样,只是左右用递归来表示。那么题解如下:
后序遍历也一模一样,先记录root.left.val,再记录root.right.val,最后记录root.val。而helper的作用上文也有所提及,无论是什么形式,它的作用就是记录当前节点的value,因此左右val可以用helper代替。题解如下:
迭代法:
对于迭代法,思路和递归是一样的。其实递归就是隐式栈,那么如果迭代,就要显示使用栈,来存储我们要处理的顺序。首先考虑前序遍历,前序是先当前,再左,最后右,那么我们使用迭代,也要谨记这个顺序。使用栈,最主要就是考虑什么时候元素入栈,什么时候出栈。出栈比较好理解,每次出栈就记录它的值即可,这样记录了值,就不需要它了,也就出栈了。关键在于入栈,因为前序遍历是中左右,那么出栈记录了值就可以看成中,因为我们必须要它在最前面。而之后就需要记录它们的左和右,那么此时就需要先把右边压入栈(因为先进后出),左边再压入栈。这样出栈的话,就是左边先出来,被记录值,然后右边再出来,再被记录值。那么题解如下:
对于后序遍历,它的顺序是左右中。而我们已经会了中左右,那么中右左就一样了,这样反过来就是后序遍历。题解如下:
对于中序遍历,其实思路也一样,但是难点在于,开头并不是root,而是最左面的一个节点。之前的方法都是在stack里面存一个root,然后从它开始出栈被记录。而这次对于中序遍历,我们不能从它开始,而是应从它的最左边开始,那么每次考虑一个节点,应该先把它的左边全部压入栈内,作为优先考虑。等到考虑到它,也即它出栈时,那么它的左边也一定全部考虑完了,此时再把它的右边压入栈,但是压入之前同样先把它的全部左边加入。那么题解如下: