# 34.traversal-of-binary-tree

## 二叉树遍历系列

[144.二叉树的前序遍历](https://suki.gitbook.io/91-days-algorithm/medium/hot/pages/-MNgQjuxfp7u0PyxDmjT#144.二叉树的前序遍历)

[94.二叉树的中序遍历](https://suki.gitbook.io/91-days-algorithm/medium/hot/pages/-MNgQjuxfp7u0PyxDmjT#94.二叉树的中序遍历)

[145.二叉树的后序遍历](https://suki.gitbook.io/91-days-algorithm/medium/hot/pages/-MNgQjuxfp7u0PyxDmjT#145.二叉树的后序遍历)

[102.二叉树的层序遍历](https://suki.gitbook.io/91-days-algorithm/medium/hot/pages/-MNgQjuxfp7u0PyxDmjT#102.二叉树的层序遍历)

**扩展**

* 多叉树的遍历，参考[图的遍历](https://github.com/suukii/Articles/blob/master/articles/dsa/dsa_graph.md#%E5%9B%BE%E7%9A%84%E9%81%8D%E5%8E%86)
* $O(1)$ 空间的遍历，[Morris Traversal](https://github.com/suukii/Articles/blob/master/articles/dsa/dsa_binary_tree.md#morris-traversal)

## 144.二叉树的前序遍历

### 递归

#### 思路

1. 首先打印 `root`
2. 然后遍历左子树的所有节点
3. 再遍历右子树的所有节点

> 在遍历顺序中，根节点是在**最前面**的，所以叫做**前序遍历**。

```
print(root)
preorder(root.left)
preorder(root.right)
```

#### 复杂度分析

* 时间复杂度：$O(2^h)$，h 为二叉树的高度。也可以表示为 $O(n)$，n 为二叉树的节点数。
* 空间复杂度：$O(h)$，h 为二叉树的高度。

#### 代码

JavaScript Code

```javascript
/**
 * Definition for a binary tree node.
 * function TreeNode(val) {
 *     this.val = val;
 *     this.left = this.right = null;
 * }
 */
/**
 * @param {TreeNode} root
 * @return {number[]}
 */
var preorderTraversal = function (root, res = []) {
    if (!root) return [];

    res.push(root.val);
    root.left && preorderTraversal(root.left, res);
    root.right && preorderTraversal(root.right, res);
    return res;
};
```

### 迭代

#### 思路

1. 使用一个栈来实现 DFS，首先把 root 入栈，
2. 当栈不为空时，弹出栈顶元素 node，然后把 node 的左右节点分别入栈，接着把 node 的值存到结果数组 res，
3. 当栈为空时遍历结束。

#### 代码

JavaScript Code

```javascript
/**
 * Definition for a binary tree node.
 * function TreeNode(val) {
 *     this.val = val;
 *     this.left = this.right = null;
 * }
 */
/**
 * @param {TreeNode} root
 * @return {number[]}
 */
var preorderTraversal = function (root) {
    const stack = [root];
    const res = [];

    while (stack.length > 0) {
        const node = stack.pop();
        if (node) {
            res.push(node.val);
            stack.push(node.right, node.left);
        }
    }
    return res;
};
```

## 94.二叉树的中序遍历

### 递归

#### 思路

1. 首先遍历左子树的所有节点
2. 打印 `root`
3. 再遍历右子树的所有节点

> 在遍历顺序中，根节点是在**中间**的，所以叫做**中序遍历**。

```
inorder(root.left)
print(root)
inorder(root.right)
```

#### 代码

JavaScript Code

```javascript
/**
 * Definition for a binary tree node.
 * function TreeNode(val) {
 *     this.val = val;
 *     this.left = this.right = null;
 * }
 */
/**
 * @param {TreeNode} root
 * @return {number[]}
 */
var inorderTraversal = function (root, res = []) {
    if (!root) return [];
    root.left && inorderTraversal(root.left, res);
    res.push(root.val);
    root.right && inorderTraversal(root.right, res);
    return res;
};
```

### 迭代

#### 思路

1. 二叉树的中序遍历是 left -> root -> right
2. 我们从 root 开始遍历，把 root 入栈，然后 root.left 入栈，然后 root.left.left 入栈，...，直到遍历到最深层的叶子左节点
3. 开始将栈顶元素弹出，如果该元素没有右子节点，说明这是个左子节点，直接放入 res 数组，
4. 如果该元素有右子节点，说明这还是这个父节点，把父节点放入 res 数组，然后把它的右子节点当成新的 root 重复步骤 2-4。

#### 代码

JavaScript Code

```javascript
/**
 * Definition for a binary tree node.
 * function TreeNode(val) {
 *     this.val = val;
 *     this.left = this.right = null;
 * }
 */
/**
 * @param {TreeNode} root
 * @return {number[]}
 */
var inorderTraversal = function (root) {
    const stack = [];
    const res = [];

    while (root || stack.length > 0) {
        while (root) {
            stack.push(root);
            root = root.left;
        }
        root = stack.pop();
        res.push(root.val);
        root = root.right;
    }
    return res;
};
```

## 145.二叉树的后序遍历

### 递归

#### 思路

1. 首先遍历左子树的所有节点
2. 然后遍历右子树的所有节点
3. 最后打印 `root`

> 在遍历顺序中，根节点是在**最后面**的，所以叫做**后序遍历**。

```
postorder(root.left)
postorder(root.right)
print(root)
```

#### 代码

JavaScript Code

```javascript
/**
 * Definition for a binary tree node.
 * function TreeNode(val) {
 *     this.val = val;
 *     this.left = this.right = null;
 * }
 */
/**
 * @param {TreeNode} root
 * @return {number[]}
 */
var postorderTraversal = function (root, res = []) {
    if (!root) return [];

    root.left && postorderTraversal(root.left, res);
    root.right && postorderTraversal(root.right, res);
    res.push(root.val);

    return res;
};
```

### 迭代

#### 思路 1

这是比较讨巧的思路。

1. 二叉树的前序遍历是 root -> left -> right
2. 二叉树的后序遍历是 left -> right -> root
3. 可以看到后序遍历差不多是前序遍历的结果倒转过来，我们可以把前序遍历的套路拿过来稍做改动。
4. 只需把第 2 步把 node 存入 res 这一步由 `res.push(node.val)` 改为 `res.unshift(node.val)`，并且将左右子节点入栈的顺序调换一下即可。

#### 代码 1

JavaScript Code

```javascript
/**
 * Definition for a binary tree node.
 * function TreeNode(val) {
 *     this.val = val;
 *     this.left = this.right = null;
 * }
 */
/**
 * @param {TreeNode} root
 * @return {number[]}
 */
var postorderTraversal = function (root) {
    const stack = [root];
    const res = [];
    while (stack.length > 0) {
        const node = stack.pop();
        if (node) {
            stack.push(node.left, node.right);
            res.unshift(node.val);
        }
    }
    return res;
};
```

#### 思路 2

这是比较正经的思路。

因为后序遍历的顺序是 left -> right -> root，我们需要先遍历 left，但如果直接遍历，访问完 left 之后没办法回到 root 了，因为二叉树的子节点是没有指向父节点的指针的。

所以，在遍历 left 之前，我们需要把 root 和 right 存起来，访问完 left 之后再回到 root，这时候需要判断，如果 root.right 存在且还没访问过，就先去访问 root.right，否则直接打印 root 的值。

1. 将 root.right 和 root 入栈，稍后遍历。
2. 让 `root = root.left` (相当于将 root 看成一个 cur 指针)。
3. 当 root 不为空时，重复步骤 1 和 2。
4. 开始出栈，`root = stack.pop()`，检查 root.right 有没有被访问过：
   1. 如果没有被访问过的话，这时候 root.right 应该在栈顶，将 root.right 弹出，然后将 root 压回栈等最后再遍历。接着让 `root = root.right`，开始处理右子树。
   2. 如果已经被访问过，或者 root.right 为空的话，这时我们直接打印 root 的值就好了。

#### 代码 2

JavaScript Code

```javascript
/**
 * Definition for a binary tree node.
 * function TreeNode(val) {
 *     this.val = val;
 *     this.left = this.right = null;
 * }
 */
/**
 * @param {TreeNode} root
 * @return {number[]}
 */
var postorderTraversal = function (root) {
    if (!root) return [];

    const stack = [];
    const res = [];

    while (true) {
        while (root) {
            root.right && stack.push(root.right);
            stack.push(root);
            root = root.left;
        }

        root = stack.pop();

        if (root.right && root.right === stack[stack.length - 1]) {
            const right = stack.pop();
            stack.push(root);
            root = right;
        } else {
            res.push(root.val);
            root = null;
        }

        if (!stack.length) break;
    }

    return res;
};
```

## 102.二叉树的层序遍历

### 迭代

#### 思路

1. 可以利用一个队列来简化遍历操作，首先将根节点入列；
2. 接着开始出列，再分别把出列节点的左右子节点入列，重复这一步的操作直到所有节点都被遍历了；
3. 问题是，如何识别当前行是否已经遍历完成？我们可以使用一个特殊标识符(比如 `null`)；
4. 在将根节点入列后，马上入列一个 `null` 作为第一行的结束标识；
5. 接着根节点出列，根节点的左右子节点入列；接着 `null` 出列，说明第一行已经遍历结束，这时候队列里的都是第二行的节点，此时我们再入列一个 `null` 作为第二行的结束标识，再次开始出列，重复这个操作直到队列为空；
6. 注意，我们入列 `null` 的时候要先判断队列当前是否为空，如果队列为空就不要入列了，不然会无限循环的。

**关键点**

1. 用一个特殊标识符 `null` 来表示每一行的末尾；
2. 如果不想使用特殊标识符，可以在遍历每一行之前记录队列当前的长度，参考下方的 Python Code。

#### 复杂度分析

* 时间复杂度：$O(n)$，n 为树的节点数。
* 空间复杂度：$O(2^h)$，h 为树的高度，队列的最大长度是最深一层叶子节点的总数 $2^(h-1)$。

#### 代码

JavaScript Code

```javascript
/**
 * Definition for a binary tree node.
 * function TreeNode(val, left, right) {
 *     this.val = (val===undefined ? 0 : val)
 *     this.left = (left===undefined ? null : left)
 *     this.right = (right===undefined ? null : right)
 * }
 */
/**
 * @param {TreeNode} root
 * @return {number[][]}
 */
var levelOrder = function (root) {
    if (!root) return [];

    const result = [];
    const queue = [root, null];
    let level = 0;

    while (queue.length > 0) {
        const node = queue.shift();

        if (node) {
            result[level] || (result[level] = []);
            result[level].push(node.val);
            node.left && queue.push(node.left);
            node.right && queue.push(node.right);
        } else {
            queue.length > 0 && queue.push(null);
            level++;
        }
    }
    return result;
};
```

Python Code

```python
# Definition for a binary tree node.
# class TreeNode(object):
#     def __init__(self, val=0, left=None, right=None):
#         self.val = val
#         self.left = left
#         self.right = right
class Solution(object):
  def levelOrder(self, root):
    """
    :type root: TreeNode
    :rtype: List[List[int]]
    """
    if root == None: return []

    items = []
    queue = [root]
    level = []

    while len(queue) > 0:
      size = len(queue)
      level = []

      for i in range(size):
        node = queue.pop(0)

        if node != None:
          level.append(node.val)
          if node.left != None: queue.append(node.left)
          if node.right != None: queue.append(node.right)

      items.append(level)
    return items
```

### 递归

#### 思路

多加一个变量来标识当前遍历的深度。

#### 复杂度分析

* 时间复杂度：$O(n)$, n 为二叉树的节点数。
* 空间复杂度：$O(n)$, n 为二叉树的节点数，`res` 数组的空间是 $O(n)$，调用栈的空间最大是 $O(h)$，h 是二叉树的深度，因为 $n >= h$，所以最后还是 $O(n)$。

#### 代码

JavaScript Code

```javascript
/**
 * Definition for a binary tree node.
 * function TreeNode(val) {
 *     this.val = val;
 *     this.left = this.right = null;
 * }
 */
/**
 * @param {TreeNode} root
 * @return {number[][]}
 */
var levelOrder = function (root, depth = 0, res = []) {
    if (!root) return [];

    res[depth] || (res[depth] = []);
    res[depth].push(root.val);

    levelOrder(root.left, depth + 1, res);
    levelOrder(root.right, depth + 1, res);

    return res;
};
```


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://suki.gitbook.io/91-days-algorithm/medium/hot/34.traversal-of-binary-tree.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
