数据结构与算法系列——二叉树系列题目(1)

504 阅读4分钟

这是我参与更文挑战的第3天,活动详情查看: 更文挑战

前言

身为前端开发人员,平时项目中写代码用的最多的就是 数组、集合、对象。这3种数据结构,但是作为程序员这肯定时不够的。但是在我们看博客、源码解读都会看到深度优先遍历, 广度优先遍历、前序、中序、后序。所以理解它势在必行哇,本篇文章大概会带你用JS去模拟树形结构、用递归、或者是栈、或者是队列,去遍历树。 本篇文章代码在我的github 上。

定义

在计算机科学中,树是一种重要的非线性数据结构,直观的看,它是数据元素按分支关系组织起来的结构。二叉树是每个节点最多有两个子树的有序树。通常子树的根被称作“左子树”和“右子树”。二叉树常被用做二叉查找树和二叉堆或是二叉排序树。二叉树的每个节点至多只有两颗子树,二叉树有左右之分,次序不能颠倒。

二叉树有两个子树,对应js就是两个指针,还有个初始的值。OK 我们接下来用代码去模拟这个过程。

这样我们就去模拟了树的节点,OK我们接下来就构造一棵树,然后去遍历一颗树。

前序、中序、后序遍历的两种方式

假设前提都是先遍历左子树的前提下, 前中后其实就是对应根节点在的位置
前序遍历 按照的是 根节点 - 左子树 - 右子树
中序遍历按照的是  左子树 - 根节点 - 右节树
后序遍历按照的是  左子树- 右子树 - 根节点

递归版本

解读一下这段代码, total 存在的意义就是为了保持所有数据,在递归调用的时候能够保存。 递归结束的条件 就是当调用的节点是叶子节点 return 就好了。 然后不断回溯就可以获得相应的结果。

栈版本

上面二叉树的3种遍历方式本质函数递归,递归就是函数调用自己,对于js 而言会维护一个函数调用栈, 每调用一次函数,就会将当前函数的执行上下文push到栈中,然后等待函数执行结束就会pop出来。所以递归的本质其实栈。

所以不断地去模拟入栈出栈这个过程。 

其实这边比较难理解的是中序遍历吧,其实他也是模拟递归的过程, 前序和后序都是从个根节点所在的位置去推理。

DFS 和 BFS

dfs 和 bfs 分别是深度优先遍历 和 广度优先遍历

dfs 深度优先遍历其实本质 就是前序遍历,大家把前面两种遍历方式认真去理解一遍就是非常OK的。 我主要和大家探讨下广度优先遍历, 何为广度优先遍遍历就是一层一层去遍历, 这里我们可以利用队列的 先进先出的特点带大家去领略其中的奥妙。 

如果要你记录每一层的数据呢,其实就是bfs 的拓展? 二叉树从根节点开始每一层至少有0-2 个节点,不断往下每一层的节点就是不断乘以2的(假设每个节点都有左右两个) 所以呢我们肯定要循环的往队列的加东西不然循环的个数 如何确立

const levelOrder = function(root) {
    // 初始化结果数组
    const res = []  
    // 处理边界条件
    if(!root) {
        return res
    }  
    // 初始化队列
    const queue = []   
    // 队列第一个元素是根结点
    queue.push(root)  
    // 当队列不为空时,反复执行以下逻辑
    while(queue.length) {
        // level 用来存储当前层的结点
        const level = []  
        // 缓存刚进入循环时的队列长度,这一步很关键,因为队列长度后面会发生改变
        const len = queue.length  
        // 循环遍历当前层级的结点
        for(let i=0;i<len;i++) {
            // 取出队列的头部元素
            const top = queue.shift()  
            // 将头部元素的值推入 level 数组
            level.push(top.val)
            // 如果当前结点有左孩子,则推入下一层级
            if(top.left) {
                queue.push(top.left)
            }
            // 如果当前结点有右孩子,则推入下一层级
            if(top.right) {
                queue.push(top.right)
            }
        }
        // 将 level 推入结果数组
        res.push(level)
    }
    // 返回结果数组
    return res
};

求树的深度

求树的高度,也就是递归左子树和右子树哪一个的深度,但是也不要忘记自身根节点+1。

翻转二叉树(二叉树的镜像)

二叉树的镜像其实就是在递归过程中二叉树的左右节点互换,然后就OK了哈哈哈哈哈。

总结

暂时先写这么多吧,写多了怕大家难以掌握,第一篇主要是树的 三种遍历方式,加上广度优先遍历掌握,这个掌握 了,其实其他二叉树的题目都是纸老虎,主要体会递归的回溯的魅力, 以及栈和队列这个数据结构,多写写你会发现数据结构和算法的关联性还是很大的,继续加油吧💪! 本篇例子都在我的github 欢迎star, 码子不易, 觉得对你有收获的可以点个👍不。