JS数据结构-树

1.什么是树?

  • 树是一种分层数据的抽象模型
  • 前端常见的树包括:DOM树,级联选择,树形控件
    在这里插入图片描述

2.二叉树常用操作

  1. 先序遍历
  2. 中序遍历
  3. 后序遍历
  4. 深度优先遍历
  5. 广度优先遍历

3.代码示例

定义一个二叉树文件

const bt = { 
    val: 1,
    left: { 
        val: 2,
        left: { 
            val: 4,
            left: null,
            right: null
        },
        right: { 
            val: 5,
            left: null,
            right: null
        }
    },
    right: { 
        val: 3,
        left: { 
            val: 6,
            left: null,
            right: null,
        },
        right: { 
            val: 7,
            left: null,
            right: null
        }
    }
}

module.exports = bt;

1.先序遍历

//递归版 
const preorder = (root) => { 
    if(!root) {return;}
    console.log(root.val);
    preorder(root.left);
    preorder(root.right);
}

//非递归版
const preorder = (root) => { 
    if(!root) {return;}
    //先把根节点压入栈
    const stack = [root];
    while(stack.length) { 
        const n = stack.pop();
        //先序遍历先访问根节点
        console.log(n.val);
        //然后依次访问左和右
        //因为栈是先进后出,所以先把右节点压如,这样才能先访问到左节点
        if(n.right) stack.push(n.right);
        if(n.left) stack.push(n.left);
    }
    // 具体思路就是弹出根节点,压入右节点,压入左节点,反复循环
}

2.中序遍历

//递归版
const inorder = (root) => { 
    if(!root) {return;}
    inorder(root.left);
    console.log(root.val);
    inorder(root.right);
}

//非递归版
const inorder = (root) => { 
    if(!root) {return;}
    const stack = [];
    //定义一个指针指向根节点
    let p = root;
    while(stack.length || p) { 
        // 先把根节点和左节点依次入栈
        while(p) {
            stack.push(p);
            p = p.left;
        }
        // 弹出栈顶元素,也就是最尽头左边节点
        const n = stack.pop();
        console.log(n.val);
        // 指针指向最尽头 左节点的右节点,如果有右节点就压入栈中(因为此时最尽头的左节点是他的右节点的中间节点)
        // 此时对右节点来说又是一次全新先把所有左节点入栈过程
        p = n.right;
        // 具体思路就是把根节点入栈,再把所有左节点的全部入栈,把右节点当做一次全新的根节点
    }
}

3.后序遍历

//递归版
const postorder = (root) => { 
    if(!root) {return;}
    postorder(root.left);
    postorder(root.right);
    console.log(root.val);
}
//非递归版
const postorder = (root) => { 
    if(!root) {return;}
    const outputStack = [];
    const stack = [root];
    while(stack.length) { 
        const n = stack.pop();
        outputStack.push(n);
        if(n.left) stack.push(n.left);
        if(n.right) stack.push(n.right);
    }
    while(outputStack.length) {
        const n = outputStack.pop();
        console.log(n.val);
    }
    // 思路,要实现左右中,我们可以把倒过来变成中右左,就变成了一个和先序遍历很像的遍历
    // 把先序遍历的访问操作改变为入栈操作,利用栈的先进后出,再把子节点全部逆序出来访问,就实现了后续遍历
}

5.深度优先遍历

//定义一个树
 const tree = {
     val: 'a',
     children: [
         {
            val: 'b',
            children: [
                { 
                    val: 'd',
                    children: []
                },
                { 
                    val: 'e',
                    children: []
                }
            ]
         },
         { 
            val: 'c',
            children: [
                {
                    val: 'f',
                    children: []
                },
                {
                    val: 'g',
                    children: []
                }
            ]
         }
     ]
 }

 //深度优先遍历
 const dfs = (root) => {  
     console.log(root.val);
     root.children.forEach(dfs)
 }

 dfs(tree);

6.广度优先遍历

const tree = {
    val: 'a',
    children: [
        {
           val: 'b',
           children: [
               { 
                   val: 'd',
                   children: []
               },
               { 
                   val: 'e',
                   children: []
               }
           ]
        },
        { 
           val: 'c',
           children: [
               {
                   val: 'f',
                   children: []
               },
               {
                   val: 'g',
                   children: []
               }
           ]
        }
    ]
}
//广度优先遍历
const bfs = (root) => { 
    const q = [root];
    while(q.length > 0) { 
        const n = q.shift();
        console.log(n.val);
        n.children.forEach(child => { 
            q.push(child);
        })
    }
}
bfs(tree);

4.LeetCode

接下来使用树这个数据结构来刷LeetCode有关树的题目,巩固提升对树的了解。

题号104.二叉树的最大深度(简单)

题目要求:
在这里插入图片描述

解题思路:
使用深度优先遍历,并记录每个节点所在层级,定义一个新的变量来记录根节点层级,当遇到新的根节点时,刷新这个变量为刷大值

编写代码

var maxDepth = function(root) {
    let res = 0;
    //定义深度优先遍历
    const dfs = (n,l) => { 
        if(!n) return;
        //遇到叶子节点时刷新res
        if(!n.left && !n.right) {
            res = Math.max(res,l);
        }
        dfs(n.left,l+1);
        dfs(n.right,l+1);
    }
   
    dfs(root,1);
    return res;
};

复杂度分析

  • 时间复杂度:O(n) n是树的结点数
  • 空间复杂度:O(n) n是树的结点数

题号111.二叉树的最小深度(简单)

题目要求:
在这里插入图片描述

解题思路:
使用广度优先遍历,并记录深度,当找到第一个子节点时,就可以返回最小深度了
编写代码

var minDepth = function(root) {
    if (!root) { return 0; }
    const q = [[root,1]];
    while (q.length) {
        const [n,l] = q.shift();
        if( !n.left && !n.right) {
            return l;
        }
        if(n.left) q.push([n.left, l + 1]);
        if(n.right) q.push([n.right, l + 1]);
    }
};

复杂度分析

  • 时间复杂度:O(n)
  • 空间复杂度:O(n)

题号102.二叉树的层序遍历(中等)

题目要求:
在这里插入图片描述

解题思路:
广度遍历,按层加入队列中,确保每层的元素都进入自己对应的数组后,再新建一个数组放下一层的元素。
编写代码

var levelOrder = function(root) {
    if(!root) return [];
    const res = [];
    const q = [root];
    //当队列非空时,将队列压入返回值中
    while(q.length) {
        let len = q.length;
        res.push([]);
        //确保取到队列中同一层元素
        while(len--) {
            const n = q.shift();
            res[res.length-1].push(n.val);
            if(n.left) q.push(n.left);
            if(n.right) q.push(n.right);
        }
    }
    return res;
};

复杂度分析

  • 时间复杂度:O(n) n是结点数
  • 空间复杂度:O(n) n是结点数
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值