Zer0e's Blog

二叉树的前中后序遍历

字数统计: 1.7k阅读时长: 7 min
2020/03/30 Share

前言

最近在看面试题,基本已经知道算法这块地方是自己的薄弱点,特意复习记录一下。
今天写的是二叉树的前中后序遍历,我会分别用递归还有迭代的方法实现。

正文

前序遍历

来源LeetCode第144题。

递归

官方特意在题目中标注递归算法很简单,那么就先来看看递归算法。
递归算法就是先得到结点值,然后递归遍历左子树和右子树。
python代码如下

1
2
3
4
5
6
7
8
9
10
class Solution:
def preorderTraversal(self,root):
if not root:
return []
result = []
result.append(root.val)
# 由于递归返回list,需要使用extend合并两个列表。
result.extend(self.preorderTraversal(root.left))
result.extend(self.preorderTraversal(root.right))
return result

Java代码如下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public class Solution {
List<Integer> result = new ArrayList<Integer>();
public List<Integer> preorderTraversal(TreeNode root) {
preOrder(root);
return result;
}
public void preOrder(TreeNode root){
if(root == null){
return;
}
result.add(root.val);
preOrder(root.left);
preOrder(root.right);
}
}

与python大同小异。

迭代

我们知道递归的本质是使用栈来实现,那么我们也可以使用一个栈来帮助我们进行迭代。
而栈的进入顺序为先右子树然后左子树,因为栈是先进后出的数据结构,所以我们让右子树先进栈,让左子树先出栈,才能实现root-left-right的顺序。
python代码如下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
class Solution(object):
def preorderTraversal(self, root):
if not root:
rerurn []
stack = []
result = []
stack.append(root)
while stack:
node = stack.pop()
if not node:
continue
result.append(node.val)
stack.append(node.right)
stack.append(node.left)
return result

Java代码如下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
public class Solution {
public List<Integer> preorderTraversal(TreeNode root) {
if(root == null){
return new ArrayList<Integer>();
}
List<Integer> result = new ArrayList<Integer>();
Stack<TreeNode> stack = new Stack<TreeNode>();
stack.push(root);
while(!stack.isEmpty()){
TreeNode temp = stack.pop();
result.add(temp.val);
if(temp.right != null){
stack.push(temp.right);
}
if(temp.left != null){
stack.push(temp.left);
}
}
return result;
}
}

中序遍历

来自LeetCode第94题。
中序遍历是left-root-right的顺序,递归方法依旧很简单。

递归

python代码如下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
class Solution:
def inorderTraversal(self, root: TreeNode) -> List[int]:
res = []
def inorder(root):
if root == None:
return
# 可以省略判断
if root.left != None:
inorder(root.left)
res.append(root.val)
if root.right != None:
inorder(root.right)
inorder(root)
return res

其实可以省略判断,因为函数开始就已经判断root是否为空。

Java代码如下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
class Solution {
public List<Integer> inorderTraversal(TreeNode root) {
List<Integer> result = new ArrayList<>();
helper(root,result);
return result;
}
public void helper(TreeNode root,List<Integer> result){
if (root == null){
return;
}
helper(root.left,result);
result.add(root.val);
helper(root.right,result);
return;
}
}

迭代

中序遍历的迭代方法与前序遍历难一些。中序遍历需要先将所有左子树压入栈中,然后开始从二叉树左下角的节点开始出栈,将本节点的的值放入结果中,然后将该节点的右子树放入栈中。之后便可按照left-root-right的顺序将值放入结果中。
python代码如下

1
2
3
4
5
6
7
8
9
10
11
12
13
class Solution:
def inorderTraversal(self, root: TreeNode) -> List[int]:
stack = []
res = []
while True:
while root:
stack.append(root)
root = root.left
if not stack:
return res
root = stack.pop()
res.append(root.val)
root = root.right

Java代码如下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
class Solution {
public List<Integer> inorderTraversal(TreeNode root) {
Stack<TreeNode> stack = new Stack<TreeNode>();
List<Integer> result = new ArrayList<>();
while (true){
while (root != null){
stack.push(root);
root = root.left;
}
if (stack.isEmpty()){
return result;
}
root = stack.pop();
result.add(root.val);
root = root.right;
}
}
}

我们可以稍微修改下代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
class Solution {
public List<Integer> inorderTraversal(TreeNode root) {
Stack<TreeNode> stack = new Stack<TreeNode>();
List<Integer> result = new ArrayList<>();
while (!stack.isEmpty() || root != null){
if (root != null){
stack.push(root);
root = root.left;
}else{
root = stack.pop();
result.add(root.val);
root = root.right;
}

}
return result;
}
}

后序遍历

LeetCode第145题。前中后序遍历官方给的难度是递增的。后序遍历难度为困难。

递归

当然我们如果使用递归的话就比较简单。与前序中序相似,只改变一下节点值加入结果的顺序。
python代码如下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
class Solution:
def postorderTraversal(self, root: TreeNode) -> List[int]:
res = []
def postorder(root):
if root == None:
return
# 可以省略判断
if root.left != None:
postorder(root.left)
if root.right != None:
postorder(root.right)
res.append(root.val)
inorder(root)
return res

Java代码类似

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
class Solution {
public List<Integer> postorderTraversal(TreeNode root) {
List<Integer> result = new ArrayList<>();
helper(root,result);
return result;
}
public void helper(TreeNode root,List<Integer> result){
if (root == null){
return;
}
helper(root.left,result);
helper(root.right,result);
result.add(root.val);
return;
}
}

迭代

困难主要在于使用迭代实现后序遍历。后序遍历的顺序为left-right-root,由于我们先获得的是root节点,之后才能获取左右子树,所以我们改变思路,
我们可以先从root-right-left的顺序下手,之后再反序,就可以得到后序遍历的结果。
由于我们使用的是栈,我们先把根节点放入栈中,然后依次放入左子树和右子树,这样栈的弹出顺序就是由右子树到左子树,那么我们的思路就确定了:
先将根节点push到栈中,pop出来后将值存入结果中,然后分别push左右子树到栈中,右子树先出栈,得到了右子树的值,然后得到左子树的值,即遍历顺序就为根-右子树-左子树。
因此,我们的入栈顺序就是为root-left-right
python代码如下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
class Solution:
def postorderTraversal(self, root: TreeNode) -> List[int]:
stack = [root]
res = []
if not root:
return []
while stack:
node = stack.pop()
if not node:
continue
res.append(node.val)
stack.append(node.left)
stack.append(node.right)
return res[::-1]

Java代码如下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
class Solution {
public List<Integer> postorderTraversal(TreeNode root) {
if(root == null){
return new ArrayList<Integer>();
}
List<Integer> result = new ArrayList<Integer>();
Stack<TreeNode> stack = new Stack<TreeNode>();
stack.push(root);
while(!stack.isEmpty()){
TreeNode temp = stack.pop();
if (temp == null){
continue;
}
result.add(temp.val);
stack.push(temp.left);
stack.push(temp.right);

}
Collections.reverse(result);
return result;
}
}

总结

这次总结了三种二叉树的遍历方式,相比于之前,确实又加深了不少理解。三种遍历方式使用递归方法都非常简单,但是使用迭代方法就有些难度了,
其中前序遍历最简单,只要将root-right-left依次放进栈中便可得到结果。
而后序遍历与前序遍历多了一个翻转的思想,入栈顺序为root-left-right,之后反转结果,便可得到后序遍历的结果。
最后是中序遍历,将所有左子树放入栈中,然后从左下角的节点开始出栈,每当出栈后,把节点值放入结果中,然后将右子树放入栈中,如此迭代,便可得到left-root-right的顺序。

CATALOG
  1. 1. 前言
  2. 2. 正文
    1. 2.1. 前序遍历
      1. 2.1.1. 递归
      2. 2.1.2. 迭代
    2. 2.2. 中序遍历
      1. 2.2.1. 递归
      2. 2.2.2. 迭代
    3. 2.3. 后序遍历
      1. 2.3.1. 递归
      2. 2.3.2. 迭代
  3. 3. 总结