树的遍历

133 阅读3分钟

树的遍历 leetCode

截屏2022-01-14 下午2.18.35.png

前序遍历

根节点最先访问 先访问根节点 --> 再遍历左子树 --> 最后遍历右子树

前序遍历顺序是: F B A D C E G I H

  • 递归法

时间复杂度:O(n),其中 n 是二叉树的节点数,每个节点恰好被遍历一次

空间复杂度:O(n),为递归过程中栈的开销,平均情况下为O(Logn),最坏情况数呈现链状结构,为O(n)

function preorderTraversal(root: TreeNode | null): number[] {
    const values: number[] = [];
    //先构造前序遍历: 递归结构
    const preDfs = (node: TreeNode) => {
        if (node === null) {
            return;
        }
        //1. 先访问根节点
        values.push(node.val);
        //2. 再访问左子树
        if (node.left !== null) {
            preDfs(node.left);
        }
        //3. 再访问右子树
        if (node.right !== null) {
            preDfs(node.right);
        }        
    }
    //递归调用
    preDfs(root);
    //返回最终值
    return values;
};
  • 迭代法 时间复杂度O(n):每个节点都被遍历了一次

空间复杂度O(n)

function preorderTraversal(root: TreeNode | null): number[] {
    if (root === null) { return []; }
    //1.定义一个栈,使用数组来模拟一个栈,将rootNode作为初始值,压入栈
    const stack: TreeNode[] = [root];

    //2.迭代,当栈中有元素时,就出栈。然后将该元素的两个节点压入栈中,
    //注意,因为栈是先进后出,所以先压入右节点,再压入左节点。
    const res = [];
    while (stack.length > 0) {
        const node = stack.pop();
        res.push(node.val);
        if (node.right !== null) {
            stack.push(node.right);
        } 
        if (node.left !== null) {
            stack.push(node.left);
        }
    }

    //返回最终值
    return res;
};

中序遍历

根节点中间访问 先遍历左子树 --> 再访问根节点 --> 最后遍历右子树

中序遍历顺序是: A B C D E F G H I

  • dfs 递归法 时间复杂度:O(n),其中 n 是二叉树的节点数,每个节点恰好被遍历一次

空间复杂度:O(n),为递归过程中栈的开销,平均情况下为O(Logn),最坏情况数呈现链状结构,为O(n)

function inorderTraversal(root: TreeNode | null): number[] {
    const res: number[] = [];
    const midDfs = (node: TreeNode) => {
        if (node === null) {
            return;
        }
        if (node.left !== null) {
            dfs(node.left);
        }

        res.push(node.val);

        if (node.right !== null) {
            midDfs(node.right);
        }
    }
    midDfs(root);
    return res;
};
  • 迭代法 时间复杂度O(n):每个节点都被遍历了一次

空间复杂度O(n)

function inorderTraversal(root: TreeNode | null): number[] {
    if (root === null) {return [];}
    const res: number[] = [];
    const stack: TreeNode[] = [];
    let p = root;

    while (stack.length > 0 || p) {
        //先判断是否有节点
        while (p) {
            //有的话先压入栈,因为是中序
            stack.push(p);
            //p 指向 p.left
            p = p.left;
        }
        //取出栈顶的节点
        const node = stack.pop();
        res.push(node.val);
        //将p 指向 p = node.right
        p = node.right;
    }
    
    return res;
};

后序遍历

根节点最后访问 先遍历左子树 --> 再遍历右子树 --> 最后访问根节点

后续序遍历顺序是: A C E D B H I G F

时间复杂度:O(n),其中 n 是二叉树的节点数,每个节点恰好被遍历一次

空间复杂度:O(n),为递归过程中栈的开销,平均情况下为O(Logn),最坏情况数呈现链状结构,为O(n)

function postorderTraversal(root: TreeNode | null): number[] {
    if (root === null) { return []; }
    const res: number[] = [];
    const sufDfs = (node: TreeNode) => {
        if (node.left !== null) {
            sufDfs(node.left);
        }

        if (node.right !== null) {
            sufDfs(node.right);
        }
        res.push(node.val);
    }

    sufDfs(root);
    return res;
};
  • 迭代法 时间复杂度O(n):每个节点都被遍历了一次

空间复杂度O(n)

function postorderTraversal(root: TreeNode | null): number[] {
    if (!root) return [];
    const res = [];
    const stack = [root];
    while (stack.length) {
        const n = stack.pop();
        res.push(n.val); // 根->右->左
        n.left && stack.push(n.left);
        n.right && stack.push(n.right);
    }
    return res.reverse(); // 左->右->根
};

注意 返回值是 res.reverse();

层序遍历

一层层的从左到右遍历 遍历顺序是: F B G A D I C E H

时间复杂度O(n)

空间复杂度O(n)

//层序遍历顺序就是广度优先遍历 bfs
//不过在遍历的时候需要记录当前节点所处的层级,方便将其添加到不同的数组中
function levelOrder(root: TreeNode | null): number[][] {
    if (root === null) {
        return [];
    }
    //最终返回的二维数组
    const res: number[][] = [];
    //使用数组模拟栈
    let queue: TreeNode[] = [root];
    while (queue.length > 0) {
        //保存当前这一层的值
        const level: number[] = [];
        //拿到当前queue的长度
        const queueSize = queue.length;
        for (let i = 0; i < queueSize; i++) {
            //把队列最前的元素推出出来
            const node = queue.shift();
            //将当前节点的值保存到这一级数组中
            level.push(node.val);
            //将左右孩子压入到queue中,再下一次遍历的时候拿出来
            if (node.left) {
                queue.push(node.left);
            }
            if (node.right) {
                queue.push(node.right);
            }
        }
        //将当前层级的数组push到返回值的数组中
        res.push(level);
    }
    return res;
};