二叉树的基本知识你还记得多少?

前言

今天我们来聊聊数据结构中的二叉树结构。树是一种非线性的数据结构,而二叉树是树结构中的一种,由节点(node)和连接这些节点的边(edge)组成。

基本概念

什么是树,在现实生活中我们的树就是由一根主干加许多枝条,枝条上有许多叶子组成。那么在数据结构中我们也是这么表示的,只不过这棵树是倒过来的树。

树的定义

  • :树是一种由节点组成的层次结构,其中每个节点包含一个值或条件,节点之间通过边连接。
  • 根节点(Root):树的顶层节点,没有父节点。
  • 子节点(Child):从一个节点直接连接下来的节点称为其子节点。
  • 父节点(Parent):直接连接到一个子节点的节点称为其父节点。
  • 叶节点(Leaf):没有子节点的节点。
  • 内部节点(Internal Node):有至少一个子节点的节点。
  • 兄弟节点(Sibling):同一个父节点的子节点。

树的属性

  • 高度(Height):从根节点到叶节点的最长路径上的边数。
  • 深度(Depth):从根节点到某一节点的边数。
  • 层次(Level):树中所有深度相同的节点的集合。
  • 度(Degree):一个节点拥有的子节点的数量。树的度是所有节点中最大的度。

树的类型

  • 二叉树(Binary Tree):每个节点最多有两个子节点,称为左子节点和右子节点。

    • 完全二叉树(Complete Binary Tree):除了最后一层外,所有层都是满的,最后一层的节点从左到右依次排列。
    • 满二叉树(Full Binary Tree):每个节点要么有两个子节点,要么没有子节点。
  • 红黑树(Red-Black Tree):一种自平衡二叉搜索树,每个节点带有额外的颜色属性(红或黑),用于保持树的平衡。

二叉树的定义

在代码中,我们经常使用这两种方式来表示一颗树。

使用对象的方式

ini 复制代码
function TreeNode(val){
    this.val = val;
    this.left = null;
    this.right = null;
}

const node = new TreeNode(1)
node.left = new TreeNode(2)
node.right = new TreeNode(3)

使用数组的方式

css 复制代码
const root = {
    val: 1,
    left: {
        val: 2,
        left: {
            val: 4
        },
        right: {
            val: 5
        }
    },
    right: {
        val: 3,
        left: {
            val: 6
        },
        right: {
            val: 7
        }
    }
}

二叉树的遍历

二叉树的遍历分为深度优先(dfs)和广度优先(bfs)

深度优先是指先尽可能的深入树的分支,当无法深入时再回溯,遍历上一个分叉口的另一个分支。

广度优先是指逐层访问节点,先访问离起始节点最近的节点,一层一层遍历,就像漫出去的水一样。

接下来我们用算法来实现这两种遍历方式。使用递归的方式很简单,你能实现使用迭代的方式完成吗?

深度优先

  1. 先序遍历

先遍历根节点,再遍历左节点、右节点

scss 复制代码
function preOrder(root){
    if(!root){
        return;
    }
    console.log(root.val);
    preOrder(root.left)
    preOrder(root.right)
}
  1. 中序遍历

先遍历左节点,再遍历根节点、右节点

scss 复制代码
function midOrder(root) {
    if (!root) {
        return;
    }
    midOrder(root.left)
    console.log(root.val);
    midOrder(root.right)
}
  1. 后序遍历

先遍历左节点,再遍历右节点、根节点

scss 复制代码
function afterOrder(root) {
    if (!root) {
        return;
    }
    afterOrder(root.left)
    afterOrder(root.right)
    console.log(root.val);
}

接下来我们使用迭代的方式来实现一个先序遍历

scss 复制代码
var preorderTraversal = function(root) {
    if(!root) return []//如果是空树则返回一个空数组
    let stack = []//定义一个栈来辅助遍历
    let res = []//将结果存储并且返回
    stack.push(root)//首先将根节点入栈
    while(stack.length){
        let top = stack.pop()
        res.push(top.val);//拿到当前栈顶元素,并将值存储
        if(top.right){//如果当前出栈的节点有右节点,则入栈
            stack.push(top.right)
        }
        if(top.left){//如果当前出栈的节点有左节点,则入栈
            stack.push(top.left)
        }
    }
    return res
};

广度优先

层序遍历

一层一层遍历,逐层访问节点

scss 复制代码
function levesOrder(root){
    let queue = []
    queue.push(root)
    while(queue.length){
        let top = queue.shift()
        console.log(top.val);
        if(top.left){
            queue.push(top.left)
        }
        if(top.right){
            queue.push(top.right)
        }
    }
}
相关推荐
腾讯TNTWeb前端团队2 小时前
helux v5 发布了,像pinia一样优雅地管理你的react状态吧
前端·javascript·react.js
范文杰5 小时前
AI 时代如何更高效开发前端组件?21st.dev 给了一种答案
前端·ai编程
拉不动的猪5 小时前
刷刷题50(常见的js数据通信与渲染问题)
前端·javascript·面试
拉不动的猪5 小时前
JS多线程Webworks中的几种实战场景演示
前端·javascript·面试
FreeCultureBoy6 小时前
macOS 命令行 原生挂载 webdav 方法
前端
uhakadotcom7 小时前
Astro 框架:快速构建内容驱动型网站的利器
前端·javascript·面试
uhakadotcom7 小时前
了解Nest.js和Next.js:如何选择合适的框架
前端·javascript·面试
uhakadotcom7 小时前
React与Next.js:基础知识及应用场景
前端·面试·github
uhakadotcom7 小时前
Remix 框架:性能与易用性的完美结合
前端·javascript·面试
uhakadotcom7 小时前
Node.js 包管理器:npm vs pnpm
前端·javascript·面试