TS代码实现-二叉树的前中后层序遍历

89 阅读3分钟

一、概述

1.二叉树定义:

每个节点最多2个子树,且有左右之分

2.遍历方式:

深度优先遍历:前序遍历,中序遍历,后序遍历 (深度优先遍历,利用Stack的先进后出的特性,注意栈的出栈顺序和入栈顺序相反) 广度优先遍历:层序遍历(广度优先遍历,利用Queue的先进先出的特性)

3.遍历实现方式:

递归遍历,颜色标记迭代遍历,常规迭代遍历,Morris遍历,

二、原理及代码实现

*深度优先遍历:

1.标记迭代遍历:

1.1 颜色标记

Leetcode上的高赞题解。值得一提,能统一3种深度优先遍历的迭代的实现代码,只用按需调用入栈顺序即可。参考链接 简单说下思路: 用颜色标记节点状态,新节点为白色,已访问的节点为灰色。 如果遇到的节点为白色,则将其标记为灰色,然后将其右子节点、自身、左子节点(这里是以中序遍历为例,前序和后序类比分别按需入栈)依次入栈。 如果遇到的节点为灰色,则将节点的值输出。

enum Color{
    White, // 新节点(第一次入栈)
    Grey // 已被访问过的节点(已完成一次入栈和出栈)
}
type MarkedNode={
    color:Color,
    node:TreeNode
}
function inorderTraversal(root:TreeNode):number[]{
    if(!root) return [];
    const res:number[]=[];
    const stack:[[Color,TreeNode]]=[[Color.White,root]];
    while(stack.length>0){
        const [color,node]=stack.pop();
        if(!node) continue;
        if(color===Color.White){
            stack.push([Color.White,node.right]);
            stack.push([Color.Grey,node]);
            stack.push([Color.White,node.left]);
        }else{
            res.push(node.val);
        }
    }
    return res;
}

1.2 数据类型标记

然后,就这个解法,有一个拓展,不用颜色标记,而是直接巧妙的用另一种标识方式,也即入栈的数据类型。

思路如下: 维护一个栈,新节点将节点入栈,已访问的节点将节点的val入栈。 如果遇到的item为number类型,则将item直接输出。 若遇到的item不是number类型,则右子节点、自身、左子节点(这里是以中序遍历为例,前序和后续分别按需入栈)依次入栈。

function inorderTraversal4(root:TreeNode):number[]{
    if(!root) return [];

    const res:number[]=[];
    const stack:(number|TreeNode|null)[]=[];
    stack.push(root);
    while(stack.length>0){
        const item=stack.pop();
        if(item===null||item===undefined) continue;
        if(typeof item !== 'number'){
            stack.push(item.right);
            stack.push(item.val);
            stack.push(item.left)
        }else{
            res.push(item);
        }
    }
    return res;
}

2.递归遍历

递归最简单,直接上代码

2.1. 前序遍历:

function preorderTraversal(root: TreeNode|null):number[]{
    const res:number[]=[];
    recur(root,res);
    return res;

    function recur(root:TreeNode|null,res:number[]){
        if(!root) return null;
    
        res.push(root.val);
        root.left&&recur(root.left,res);
        root.right&&recur(root.right,res)
    }
}

2.2 中序遍历:

function midorderTraversal(root: TreeNode|null):number[]{
    const res:number[]=[];
    recur(root,res);
    return res;

    function recur(root:TreeNode|null,res:number[]){
        if(!root) return null;
        
        root.left&&recur(root.left,res);
        res.push(root.val);
        root.right&&recur(root.right,res)
    }
}

2.3 后序方式:

function postorderTraversal(root: TreeNode|null):number[]{
    const res:number[]=[];
    recur(root,res);
    return res;

    function recur(root:TreeNode|null,res:number[]){
        if(!root) return null;
        
        root.left&&recur(root.left,res);
        root.right&&recur(root.right,res);
        res.push(root.val);
    }
}

3. 迭代方式

利用栈的特性。

3.1 前序遍历:

function preorderTraversal(root: TreeNode|null):number[]{
    if(!root) return [];

    const res:number[]=[];
    const stack:TreeNode[]=[];
    stack.push(root);
    while(stack.length>0){
        const node=stack.pop();
        node&&res.push(node.val);
        node?.right&&stack.push(node?.right); // 注意这里后left,这样下层循环left先出
        node?.left&&stack.push(node?.left);
    }
    return res;
}

3.2 中序遍历:

function inorderTraversal(root: TreeNode|null):number[]{
    if(!root) return [];
    
    const res:number[]=[];

    const stack:(TreeNode|null)[]=[];
    let curr=root;
    while(stack.length>0||!!curr){
        while(!!curr){
            stack.push(curr);
            curr=curr.left;
        }
        const node:TreeNode=stack.pop();
        if(!node) continue;
        res.push(node.val);
        if(node.right){
            curr=node.right;
        }
    }
    return res;
}

3.3 后序遍历:

迭代方式:后进先出,双栈处理, stack1左右中进栈 => stack2中右左进栈

function postOrderTraversal1(root: TreeNode|null):number[]{
    if(!root) return [];
    
    const res:number[]=[];
    const stack1:TreeNode[]=[]; 
    const stack2:TreeNode[]=[]; // 中右左进栈
    stack1.push(root);
    while(stack1.length>0){
        const node=stack1.pop();
        node&&stack2.push(node);
        node?.left&&stack1.push(node?.left);
        node?.right&&stack1.push(node?.right);
    }

    while(stack2.length>0){
        const node=stack2.pop();
        node&&res.push(node.val);
    }
    return res;
}

4. Morris遍历(待续)

*广度优先遍历

4. 层序遍历

迭代方式:利用queue的先进先出的的特性来实现。

// *****4.1 输出一维数组[]
function levelOrderTraversal1(root: TreeNode|null):number[]{
    if(!root) return [];
    
    const res:number[]=[];
    const queue:TreeNode[]=[]; 
    queue.push(root);
    while(queue.length>0){
        const node=queue.shift();
        node&&res.push(node.val);
        node?.left&&queue.push(node?.left);
        node?.right&&queue.push(node?.right);
    }
    return res;
}
// *****4.2 按层输出二维数组[[]]
function levelOrderTraversal2(root: TreeNode|null):number[][]{
    if(!root) return [];
    
    const res:number[][]=[];
    const queue:TreeNode[]=[]; 
    queue.push(root);
    while(queue.length>0){
        let item:number[]=[];
        for(let i=queue.length;i>0;i--){
            const node=queue.shift();
            node&&item.push(node.val);
            node?.left&&queue.push(node?.left);
            node?.right&&queue.push(node?.right);
        }
        res.push(item);
    }
    return res;
}