JavaScript 数据结构 ==== 二叉树

news2024/9/20 22:51:00

目录

二叉树

结构

二叉树和二叉搜索树介绍

1.创建树

2.插入一个键 

3.树的遍历

中序排序

先序遍历

后序遍历

 4.搜索树中的值

5.删除节点


二叉树

在计算机科学中,二叉树是每个结点最多有两个子树的树结构。通常子树被称作“左子树”(left subtree)和“右子树”(right subtree)。二叉树常被用于实现二叉查找树和二叉堆。 一棵深度为k,且有2^k-1个节点的二叉树,称为满二叉树。这种树的特点是每一层上的节点数都是最大节点数。而在一棵二叉树中,除最后一层外,若其余层都是满的,并且最后一层或者是满的,或者是在右边缺少连续若干节点,则此二叉树为完全二叉树。具有n个节点的完全二叉树的深度为floor(log2n)+1。深度为k的完全二叉树,至少有2k-1个叶子节点,至多有2k-1个节点。。

  • 以上是书面解答 —— 下面我总结一下

结构

接下来让我们一起来探讨js数据结构中的树。这里的树类比现实生活中的树,有树干,树枝,在程序中树是一种数据结构,对于存储需要快速查找的数据非有用,它是一种分层数据的抽象模型。一个树结构包含一系列存在父子关系的节点。每个节点都有一个父节点以及零个或多个子节点。如下所以为一个树结构:)

 

和树相关的概念:1.子树:由节点和他的后代构成,如上图标示处。2.深度:节点的深度取决于它祖节点的数量,比如节点5有2个祖节点,他的深度为2。3.高度:树的高度取决于所有节点深度的最大值。

二叉树和二叉搜索树介绍

二叉树中的节点最多只能有2个子节点,一个是左侧子节点,一个是右侧子节点,这样定义的好处是有利于我们写出更高效的插入,查找,删除节点的算法。

二叉搜索树是二叉树的一种,但是它只允许你在左侧子节点存储比父节点小的值,但在右侧节点存储比父节点大的值。接下来我们将按照这个思路去实现一个二叉搜索树。

 

1.创建树

这里我们将使用构造函数去创建一个类:

class Node {
  constructor(key) {
    this.key = key;
    this.left = null;
    this.right = null;
  }
}
class BST {
  constructor(key) {
    this.root = null;
  }
}

 

  • 我们将使用和链表类似的指针方式去表示节点之间的关系
  • Node 类:这个类是二叉树中的节点,每个节点包含一个键(key)和两个指向其他节点的链接(left 和 right)。
  • BST 类:这个类代表一个二叉搜索树,它有一个根节点 root

2.插入一个键 

向树中插入一个新的节点主要有以下三部分:

  • 1.创建新节点的Node类实例
  • 2.判断插入操作是否为根节点,是根节点就将其指向根节点
  • 3.将节点加入非根节点的其他位置。
 insert(key) {
    if (this.root === null) {
      this.root = new Node(key);
    } else {
      this.insertNode(this.root, key);
    }
  }
  1. 首先,方法检查 key 是否小于当前节点的 key(即 node.key)。
  2. 如果 key 小于当前节点的 key,则需要将新节点插入到当前节点的左子树中:
    • 如果当前节点的左子节点是 null(即没有左子节点),则创建一个新的 Node 实例,并将 key 作为其键值,然后将其赋值给 node.left
    • 如果左子节点不是 null,则递归调用 insertNode 方法,将当前节点的左子节点作为新的 node 参数,继续在左子树中查找插入位置。
  3. 如果 key 大于或等于当前节点的 key,则需要将新节点插入到当前节点的右子树中
  insertNode(node, key) {
    if (key < node.key) {
      node.left === null
        ? (node.left = new Node(key))
        : this.insertNode(node.left, key);
    } else {
      node.right === null
        ? (node.right = new Node(key))
        : this.insertNode(node.right, key);
    }
  }

3.树的遍历

访问树的所有节点有三种遍历方式:中序,先序和后序。

  • 中序遍历:以从最小到最大的顺序访问所有节点
  • 先序遍历:以优先于后代节点的顺序访问每个节点
  • 后序遍历:先访问节点的后代节点再访问节点本身

根据以上的介绍,我们可以有以下的实现代码。

中序排序

中序遍历是一种按升序访问树中所有节点的遍历方法。在二叉树中,中序遍历的顺序是先访问左子树,然后访问根节点,最后访问右子树。这种遍历方式对于二叉搜索树(BST)特别有用,因为它可以保证遍历的结果是树中元素的有序列表。

中序遍历的步骤:

  1. 访问左子树,进行中序遍历。
  2. 访问根节点。
  3. 访问右子树,进行中序遍历。
  • 代码实现
  inOrderTraverse(cb) {
    this.inOrderTraverseNode(this.root, cb);
  }
  // 辅助函数
  inOrderTraverseNode(node, cb) {
    if (node !== null) {
      this.inOrderTraverseNode(node.left, cb);
      cb(node.key);
      this.inOrderTraverseNode(node.right, cb);
    }
  }

递归的调用过程是不断往左边走,当左边走不下去了,就打印节点,并转向右边,然后右边继续这个过程。
我们在迭代实现时,就可以用栈来模拟上面的调用过程。
 

1.gif

先序遍历

递归思路:先树根,然后左子树,然后右子树。每棵子树递归。在迭代算法中,思路演变成,每到一个节点 A,就应该立即访问它。 因为,每棵子树都先访问其根节点。对节点的左右子树来说,也一定是先访问根。 在 A 的两棵子树中,遍历完左子树后,再遍历右子树。
因此,在访问完根节点后,遍历左子树前,要将右子树压入栈。

先序遍历的步骤:

  1. 访问根节点。
  2. 访问左子树,进行先序遍历。
  3. 访问右子树,进行先序遍历。

数据结构—树与二叉树(Part Ⅳ)—二叉树的遍历(递归&非递归)_假设二叉树采用二叉链存储,完成以下二叉树先序遍历的非递归算法。 提示:以下 ...

 

后序遍历

后序遍历是一种先访问所有子节点,最后访问当前节点的遍历方法。在后序遍历中,根节点是最后被访问的,遍历顺序是左子树、右子树,然后是根节点。

后序遍历的步骤:

  1. 访问左子树,进行后序遍历。
  2. 访问右子树,进行后序遍历。
  3. 访问根节点。
// 后续遍历 --- 先访问后代节点,再访问节点本身
  postOrderTraverse(cb) {
    this.postOrderTraverseNode(this.root, cb);
  }

  // 后续遍历辅助方法
  postOrderTraverseNode(node, cb) {
    if (node !== null) {
      this.postOrderTraverseNode(node.left, cb);
      this.postOrderTraverseNode(node.right, cb);
      cb(node.key);
    }
  }

 

【数据结构】链式二叉树的实现(一)-阿里云开发者社区

 4.搜索树中的值

在树中有三种经常执行的搜索类型:最大值,最小值,特定的值。

  1. 每个节点的左子树上所有节点的键值都小于它的键值。
  2. 每个节点的右子树上所有节点的键值都大于它的键值。
  3. 每个节点的左、右子树也分别是二叉搜索树。

代码中的几个方法分别实现了以下功能:

  • min()minNode(node):这两个方法用于找到二叉搜索树中的最小值。最小值是树中左子树的最末端节点的键值。minNode 方法从给定的节点开始,一直向左遍历,直到找到最左端的节点,然后返回该节点的键值。

  • max()maxNode(node):这两个方法用于找到二叉搜索树中的最大值。最大值是树中右子树的最末端节点的键值。maxNode 方法从给定的节点开始,一直向右遍历,直到找到最右端的节点,然后返回该节点的键值。

  • search(key)searchNode(node, key):这两个方法用于在二叉搜索树中搜索一个特定的键值。searchNode 方法从给定的节点开始,根据键值的大小递归地在左子树或右子树中搜索,直到找到匹配的键值或遍历到空节点,表示键值不存在于树中。

具体到搜索值的过程,searchNode 方法的工作原理如下:

  1. 如果当前节点为 null,表示已经到达树的末端,但没有找到键值,返回 false
  2. 如果键值小于当前节点的键值,递归地在当前节点的左子树中搜索。
  3. 如果键值大于当前节点的键值,递归地在当前节点的右子树中搜索。
  4. 如果键值等于当前节点的键值,表示找到了匹配的键值,返回 true

这种搜索方法利用了二叉搜索树的性质,可以高效地进行查找操作,其时间复杂度通常为 O(h),其中 h 是树的高度。在平衡的二叉搜索树中,这个操作的时间复杂度接近 O(log n),其中 n 是树中节点的数量。

 // 最小值;
  min() {
    return this.minNode(this.root);
  }
  minNode(node) {
    if (node) {
      while (node && node.left !== null) {
        node = node.left;
      }
      return node.key;
    }
    return null;
  }
  // 最大值;
  max() {
    return this.maxNode(this.root);
  }
  maxNode(node) {
    if (node) {
      while (node && node.right !== null) {
        node = node.right;
      }
      return node.key;
    }
    return null;
  }
  // 搜索树中某个值
  search(key) {
    return this.searchNode(this.root, key);
  }
  // 搜索辅助方法
  searchNode(node, key) {
    if (node === null) {
      return false;
    }
    if (key < node.key) {
      return this.searchNode(node.left, key);
    } else if (key > node.key) {
      return this.searchNode(node.right, key);
    } else {
      return true;
    }
  }

5.删除节点

二叉排序树节点删除详解_二叉排序树删除节点-CSDN博客

删除二叉搜索树中的一个节点通常分为以下几个步骤:

  1. 查找要删除的节点:首先,需要找到要删除的节点。这可以通过递归遍历树来完成。

  2. 确定删除情况:找到节点后,需要确定该节点是叶子节点、只有一个子节点的节点,还是有两个子节点的节点。

  3. 删除叶子节点:如果节点是叶子节点(即没有子节点),可以直接删除该节点。

  4. 删除只有一个子节点的节点:如果节点只有一个子节点,可以删除该节点,并用其子节点来替代它。

  5. 删除有两个子节点的节点:这是最复杂的情况。如果节点有两个子节点,不能简单地删除它,因为这会破坏二叉搜索树的性质。通常的做法是找到该节点的直接前驱(通常是其右子树中的最小节点)或直接后继(通常是其左子树中的最大节点),然后将其值复制到当前节点上,接着删除原来的直接前驱或直接后继。

  // 删除
  remove(key) {
    this.root = this.removeNode(this.root, key);
  }
  findMinNode(node) {
    if (node) {
      while (node && node.left !== null) {
        node = node.left;
      }
      return node;
    }
    return null;
  }

  removeNode(node, key) {
    if (node === null) {
      return null;
    }
    if (key < node.key) {
      node.left = this.removeNode(node.left, key);
      return node;
    } else if (key > node.key) {
      node.right = this.removeNode(node.right, key);
      return node;
    } else {
      if (node.left === null && node.right === null) {
        node = null;
        return node;
      }
      if (node.left === null) {
        node = node.right;
        return node;
      } else if (node.right === null) {
        node = node.left;
        return node;
      }
      // 有两个子节点的节点
      let aux = this.findMinNode(node.right);
      node.key = aux.key;
      node.right = this.removeNode(node.right, aux.key);
      return node;
    }
  }

remove 方法是删除操作的入口,它调用 removeNode 方法来递归地删除节点。findMinNode 方法用于找到给定节点的右子树中的最小节点,这个最小节点将被用来替换当前要删除的节点。

以下是 removeNode 方法的逻辑解释:

  • 如果当前节点为空,说明没有找到要删除的节点,直接返回 null
  • 如果要删除的键值小于当前节点的键值,说明要删除的节点在当前节点的左子树中,递归地在左子树中删除。
  • 如果要删除的键值大于当前节点的键值,说明要删除的节点在当前节点的右子树中,递归地在右子树中删除。
  • 如果找到了要删除的节点(键值相等),则根据其子节点的数量来决定删除策略:
    • 如果没有子节点,直接删除该节点。
    • 如果只有一个子节点(左或右),用其子节点替换当前节点。
    • 如果有两个子节点,找到右子树中的最小节点,用该最小节点的键值替换当前节点的键值,然后删除原来的最小节点。

通过这种方式,二叉搜索树在删除节点后仍然保持其性质,即任何节点的左子树上的键值都小于该节点的键值,右子树上的键值都大于或等于该节点的键值。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2070278.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Docker Desktop镜像路径修改一直报错

一 点击Apply & Restart报错 [Window Title] Docker Desktop[Main Instruction] Error migrating WSL disk[Content] An error occurred while migrating the Docker Desktop WSL data disk to its new location:moving disk file: rename C:\Users\Lenovo\AppData\Local\D…

Linux 进程 | 进程地址空间

文章目录 进程地址空间程序地址空间进程地址空间 进程地址空间 程序地址空间 地址空间一共有如下的几个区域&#xff0c;从下到上地址逐渐增加&#xff0c;其中栈区的空间是从上往下使用&#xff0c;即从高地址往低地址增长&#xff1b;堆区的空间是从下往上使用&#xff0c;…

简单实现进度条效果(vue2)

如果用echarts或者其他图表来写个进度条有点大材小用&#xff0c;所以直接简单html、js写一下就可以&#xff1b; 以下代码基于vue2&#xff0c; 部分代码来自国内直连GPT/Claude镜像站 <template><div class"progress-container"><div class"p…

aosp源码导入android studio无法跳转-学员答疑

背景&#xff1a; 在学习framework入门课时候&#xff0c;一个很重要环节就是导入aosp的源码到android studio&#xff0c;这样有了IDE之后开发起来就很方便了&#xff0c;但是很多学员朋友对编译出来的ipr&#xff0c;iml文件改造不知道该怎么搞&#xff0c;导致android stud…

mysql 开启binlog并设置

打开my.cnf 文件&#xff0c;Linux系统文件默认位置为 /etc目录下&#xff0c;若不存在&#xff0c;可以使用下述命令查询 find / -name my.cnf修改my.cnf文件&#xff0c;开启binlog mysqld 模块下添加以下内容 server_id1 #给当前mysql机器设置一个id log-binmysql_bin …

分布式事务理论和解决方案

分布式事务理论 business&#xff08;下单&#xff09;远程调用库存&#xff08;storage&#xff09;,保存订单&#xff08;order&#xff09;&#xff0c;扣减积分&#xff08;account&#xff09;&#xff0c;只有这三个步骤全部成功&#xff0c;我们的下订单才算成功。 如果…

2024-01-开发技术积累

文章目录 递归删除文件执行任务超时时间读写锁获取异常栈信息通过NIO读取文件单例模式代码NIO管道写文件(来自nacos)NIO读取文件(来自Nacos)spring指定注解扫描 递归删除文件 xxl-job源码 public static boolean deleteRecursively(File root) {if (root ! null && ro…

JAVA中的Stream流的使用详解

1.Stream的介绍 Java 8 API添加了一个新的抽象称为流Stream&#xff0c;可以让你以一种声明的方式处理数据。Stream 使用一种类似用 SQL 语句从数据库查询数据的直观方式来提供一种对 Java 集合运算和表达的高阶抽象。Stream API可以极大提高Java程序员的生产力&#xff0c;让程…

React+Vis.js(04):vis.js设置节点显示图片

文章目录 实现效果关键代码完整代码设置图片边框和背景颜色我们继续以 复仇者联盟为例,来介绍如何实现节点显示 图片。 实现效果 以图片进行节点的显示,使得显示效果更加直观,信息更为明了。 关键代码 在vis.js中,通过属性shape来控制节点显示为图像。 const nodes …

Shire 0.5 发布:构建数据安全 RAG,充分整合研发资产

最近&#xff0c;我们发布了新版本的 Shire&#xff0c;在这个新的发布&#xff08;Shire 0.5&#xff09;里&#xff0c;你可以更好地融合本地研发资产&#xff0c;同时构建数据安全 RAG。在这次版本中&#xff0c;我们增加了&#xff1a; 对 SonarQube 的 issue 支持。可以直…

1.反爬虫机制

一、IP 封锁 网站可以检测请求的IP地址&#xff0c;并封锁那些频繁请求的IP&#xff0c;使其无法访问网站。这是一种常见的反爬虫策略&#xff0c;用于防止单个IP地址对服务器造成过大的负载。 解决办法 &#xff1a; 使用代理IP池以避免IP封锁 // 待补充 二、请求头检测(Us…

2024百元学生党蓝牙耳机有哪些?甄选四款精品王炸机型推荐

近年来&#xff0c;随着手机的普及和音乐娱乐的盛行&#xff0c;蓝牙耳机成为越来越多人的选择。蓝牙耳机的优点在于无需使用线缆&#xff0c;方便携带&#xff0c;而且可以随时随地享受音乐或者通话。在市面上&#xff0c;有各种各样的蓝牙耳机可供选择&#xff0c;对于预算有…

Star-CCM+探针查看与创建

在实际应用工况中&#xff0c;数值计算结束后为了产看某个点的标量场或矢量场可以采用探针查看。而在软件中可以通过“&#xff0c;”、“。”快捷键进行创建与查看。两者的区别具体如下所示&#xff1a; 鼠标放在要查看部件的位置&#xff0c;同时点击键盘上的“。”键&#…

神经网络动画讲解 - 神经网络工作流程

神经网络工作流程 神经网络工作流程&#xff1a; 特征分解1&#xff08;手写数字->简单笔画组合&#xff09;、特征分解2&#xff08;简单笔画->最小笔画组合&#xff09;、特征识别1&#xff08;最小笔画组合->简单笔画&#xff09;、特征识别2&#xff08;简单笔画…

Java 入门指南:迭代器(Iterator)

迭代器 迭代器&#xff08;Iterator&#xff09; 是一种行为型设计模式&#xff0c;属于设计模式之一&#xff0c;迭代器模式提供了一种方法来顺序访问一个聚合对象&#xff08;如List、Set等&#xff09;中各个元素&#xff0c;而不需要暴露该对象的内部表示。 Iterator 对象…

攻防世界-web题型-6星难度汇总-个人wp

i-got-id-200 这一题很清楚的告诉了考点是什么&#xff0c;就是cgi相关的知识&#xff0c;不过我对这个不了解。。。也不会perl语言&#xff0c;先去网上看看这个东西吧&#xff0c;了解一下 看到一篇挺有意思的视频CGI&#xff08;通用网关接口&#xff09;_百度百科 看了这…

MediaTek 天玑9000 旗舰5G,都有哪些突破

天玑9000&#xff0c;是MTK 在2022发布的全新旗舰SOC , 采用台积电4nm制程和Armv9架构,八核CPU。 市场上采用天玑9000的手机有小米12Pro、小米Redmi K50 Pro、Vivo X80等。 今天我们看下这款soc更详细的一些特点介绍。 一、率先采用台积电 4nm 先进制程 业界率先采用台积电 4nm…

传统2D3D视觉分享

哈喽&#xff0c;大家好&#xff0c;本文主要是说明关于【视觉源码小铺】知识星球内的相关内容 视觉源码小铺都有什么内容 IVision拖拽平台&算法 星球内算法&#xff08;持续更新&#xff09;

滚雪球学Java(90):Java图形界面新篇章:Swing框架深度解析,真有点东西!

咦咦咦&#xff0c;各位小可爱&#xff0c;我是你们的好伙伴——bug菌&#xff0c;今天又来给大家普及Java SE啦&#xff0c;别躲起来啊&#xff0c;听我讲干货还不快点赞&#xff0c;赞多了我就有动力讲得更嗨啦&#xff01;所以呀&#xff0c;养成先点赞后阅读的好习惯&#…

解决MySQL的PacketTooBigException异常问题

一、背景 在大数据量导入mysql的时候&#xff0c;提示错误Cause: com.mysql.cj.jdbc.exceptions.PacketTooBigException: Packet for query is too large 原因是MySQL的max_allowed_packet设置最大允许接收的数据包过小引起的&#xff0c;默认的max_allowed_packet如果不设置&…