每日一题:二叉树的遍历

137 阅读4分钟

Offer 驾到,掘友接招!我正在参与2022春招打卡活动,点击查看活动详情

树的遍历

  1. 对于树的遍历有前、中、后序遍历三种方法。
  2. 前序遍历:先遍历节点自己,再遍历当前节点的left,最后是当前节点的right。自己->left->right
  3. 中序遍历:先遍历左子树left,再遍历自己,最后是节点的right。left->自己->right
  4. 后序遍历:先遍历left,然后right,最后遍历自己。left->right->自己

下面看一个例子:

对右图进行遍历:image-20220317125500643.png
前序遍历结果4 -> 2 -> 1 -> 3 -> 7 -> 6 -> 9
中序遍历结果1 2 3 4 6 7 9
后序遍历结果1 3 2 6 9 7 4

leetcode144前序遍历

递归方法

/**
 * 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 preorderTraversal = function(root, arr=[]) {
    // 前序遍历:自己->left->right
    if (root) {
        arr.push(root.val)
        preorderTraversal(root.left,arr)
        preorderTraversal(root.right,arr)
    }
    return arr
};

上面是递归方法,我们也可以利用用迭代法来遍历:

内层遍历:设当前节点cur指向根节点,从根节点开始遍历,将当前节点缓存到栈。然后cur指向当前节点的左节点(cur=cur.left);然后记录这个节点,一直循环到没有左节点。此时就将根节点的左节点都遍历过了。这些节点也存入栈了。

外层遍历:然后从栈顶取出一个节点,找到其右节点,将cur指向右节点,再将这个右节点重新进行上面一串遍历左节点的操作。然后再循环进行这一步骤的找右节点就,进行右节点中左节点的遍历。。。

迭代法代码:

var preorderTraversal = function(root, arr=[]) {
    // 前序遍历:自己->left->right
    // 开始遍历:有一个stack栈存储
    // 节点出栈,右孩子为目标节点
    let result = []
    let stack = []
    let cur = root
    while(cur||stack.length>0){
        // 内层循环:遍历当前节点的所有left节点
        while(cur) {
        // 内层遍历将当前节点的左子树全部遍历完毕,并存下每个节点以备找其右子树
            result.push(cur.val)
            stack.push(cur) // 要通过stack中的值找其右子树
            cur = cur.left
        }
        // 外层遍历将栈顶出栈,将其右子树做当前节点,继续遍历该节点的左子树入栈出栈找右子树这一这个过程
        cur = stack.pop()
        cur = cur.right
    }
    return  result
};

以上思路讲解比较抽象,以上图树结构和前序遍历为例,详解迭代法代码过程思路:

① 先遍历根节点4(cur),将4入栈 (stack=[4])

② 遍历其左子树并入栈操作 (内层遍历):找到left为2,将2入栈 (stack=[4, 2]);继续找到左节点1,将1入栈(stack=[4, 2, 1]);再往下没有左节点了,跳出内层循环。

③ (外层遍历) :就将栈顶的节点1出栈,找其右节点为null,curl为null,stack不为空,继续循环。cur为null跳过内层循环。再进行出栈找右节点(找到栈顶2的right3),cur为节点3。

④ 当前节点为节点3。对当前节点先内层循环,记录自己的val和入栈,再找3的左子树没有,跳出内层循环;再进行外层循环的出栈找右节点,此时出栈的为节点4,cur为其右节点的7

⑤ 当前节点为7,对7进行内层遍历,一直找左节点并记录后,stack=[7, 6]。cur再找左节点为null。跳出内层循环。进行外层循环,出栈找右节点,cur为栈顶6的右节点null;再进行外层循环出栈节点7,cur为其右节点9

⑥ 当前节点为9,记录自己的值,找其左子树为null跳出内层循环,其右子树为null,且栈为空,跳出外层循环。整个遍历结束。

循环过程总结来说: 找左节点一直到底并记录当前节点 -> 没有左节点后栈顶出栈找右节点 -> 右节点变成当前节点再找当前节点的所有左节点并记录 -> 直到没有左节点后再出栈变成当前节点找到右节点 .... -> 一直到没有左节点,没有右节点,栈为空,结束整个遍历

leetcode94中序遍历

var inorderTraversal = function(root, arr=[]) {
    // // 递归
    // if (root) {
    //     inorderTraversal(root.left,arr)
    //     arr.push(root.val)
    //     inorderTraversal(root.right,arr)
    // }
    // return arr;
​
    // 迭代
    let res=[], stack=[], cur=root;
    while(cur||stack.length) {
        while(cur) {
            stack.push(cur)
            cur = cur.left  // 找到最左的值
        }
        cur = stack.pop()
        // console.log(cur.val) push最左的值,变成cur
        res.push(cur.val)
        cur = cur.right
    }
    return res
};

leetcode145后序遍历

var postorderTraversal = function(root, arr=[]) {
    // left->right->自己
    // // 递归
    // if (root) {
    //     postorderTraversal(root.left, arr)
    //     postorderTraversal(root.right, arr)
    //     arr.push(root.val)
    // }
    // return arr

    // 迭代
    let res = [], stack=[], cur = root, cache=null;
    while(cur||stack.length) {
        while(cur) {
            stack.push(cur) // 入栈
            cur = cur.left  //找到最左的值
        }
        cur = stack[stack.length-1] // 读取栈顶找right
        if(cur.right&&cur.right!=cache) {
            cur = cur.right         // 有right进行下一次循环right节点找left
        } else {
            console.log(cur.val)
            res.push(cur.val)  // 找完left和right之后再出栈记录自己
            stack.pop()  // 出栈
            cache = cur
            cur = null
        }
    }
    return res
};