实现二叉排序树

一:二叉树和二叉搜索树

  • 二叉树中的节点最多只能有两个子节点:一个是左侧子节点,另一个是右侧子节点。这个定义有助于我们写出更高效地在树中插入、查找和删除节点的算法,二叉树在计算机科学中的应用非常广泛。

  • **二叉搜索树(BST)**是二叉树的一种,但是只允许你在左侧节点存储(比父节点)小的值,在右侧节点存储(比父节点)大的值。

二:实现二叉搜索树

2.1 创建Node类表示二叉搜索树中的每个节点

    //二叉树的存储结构为
    class Node
    {
      constructor(data, left, right)
      {
        this.data = data
        this.left = left
        this.right = right
        //若有相同的元素插入节点,就放弃插入,count++
        this.count = 1
      }
    }

image-20230718103946588

该图为二叉搜索树数据结构的组织方式,对于树,我们使用两个指针,一个指向左侧子节点,一个指向右侧右节点

2.2 创建BSTree 类的基本结构

class BSTree {
    constructor() {
        this.root = null;
    }
 
    // 删除一个节点
    _removeNode(node, data) {
       
    }
 
    // 删除给定的数据节点
    remove(data) {
        this.root = this._removeNode(this.root, data);
    }
 
    // 向二叉树中插入节点
    insert(data) {
        
    }
 
    // 寻找给定数据的节点
    find(data) {
        
    }
 
    // 获得最小值的节点
    getMinNode(node = this.root) {
        
    }
 
    // 获得最大值的节点
    getMaxNode(node = this.root) {
        
    }
}

2.3 实现insert()方法

//向二叉树插入节点
insert (data)
{
    let newNode = new Node(data, null, null)
    //更新根节点的值
    if (this.root === null) {
        this.root = newNode
    } else {
        //更新当前节点的值
        let currentNode = this.root
        //父节点就是空
        let parentNode = null
        while (true) {
            //更新父节点
            parentNode = currentNode
            //判断插入节点值在左子树还是右子树
            if (newNode.data < currentNode.data) {
                //更新当前节点
                currentNode = currentNode.left
                if (!currentNode) {
                    parentNode.left = newNode
                    break
                }
            }

            else if (newNode.data > currentNode.data) {
                currentNode = currentNode.right
                if (!currentNode) {
                    parentNode.right = newNode
                    break
                }
            }

            else if (newNode.data === currentNode.data) {
                //如果相同数据 count++ 不做处理
                currentNode.count++
                break
            }
        }
    }
}
  • 首先创建一个新的节点newNode,该节点包含要插入的数据
  • 检查根节点是否为空,若为空,说明这是第一个插入的节点,将根节点指向newNode
  • 如果根节点不为空,则需要找到合适的位置插入该节点
  • 初始化当前节点currentNode为根节点this.root,并且初始化父节点parentNode为空
  • 进入循环,直到找到合适的位置插入节点或者遇到相同的数据
  • 在每次循环中,更新父节点parentNode为当前节点currentNode
  • 判断要插入的节点值和当前节点值的大小关系
    • 如果要插入的节点值小于当前节点值,说明要插入的节点应该在当前节点的左子树中
      • 更新当前节点为当前节点的左子节点 currentNode.left
      • 如果当前节点的左子节点为空,说明找到了插入位置,将新节点 newNode 设置为当前节点的左子节点,并且跳出循环
    • 如果要插入的节点值大于当前节点值,说明要插入的节点应该在当前节点的右子树中
      • 更新当前节点为当前节点的右子节点 currentNode.right
      • 如果当前节点的右子节点为空,说明找到了插入位置,将新节点 newNode 设置为当前节点的右子节点,并且跳出循环
    • 如果要插入的节点值等于当前节点值,说明要插入的节点与当前节点的值相同。
      • 将当前节点的计数器 count 加一,表示重复出现的次数。
      • 跳出循环。

2.4 实现find()方法

//寻找给定数据的节点
find (data)
{
    let currentNode = this.root
    while (currentNode) {
        if (currentNode.data == data) {
            return currentNode
        } else if (currentNode.data < data) {
            currentNode = currentNode.right
        } else {
            currentNode = currentNode.left
        }
    }
    return null
}

2.5 实现getMinNode()和getMaxNode()方法

//获取最小值
getMinNode (node = this.root)
{
    let currentNode = node
    while (currentNode.left) {
        currentNode = currentNode.left
    }
    return currentNode
}

//获取最大值
getMaxNode (node = this.root)
{
    let currentNode = node
    while (currentNode.right) {
        currentNode = currentNode.right
    }
    return currentNode
}

2.6 实现remove()方法

//删除节点,实例中不应调用
_removeNode (node, data)
{
    if (node === null) {
        return null
    }
    //找到要删除的节点的了 
    if (data === node.data) {
        //分三种情况
        //1. 要删除的节点为叶子结点 
        if (node.left === null && node.right === null) {
            return null
        }

        //2. 没有左子节点的节点
        if (node.left === null) return node.right

        //   没有右子节点的节点
        if (node.right === null) return node.left


        //3.有两个子节点的节点
        //找到待删除节点的右子树的最小值赋值给临时节点tmpNode
        //将tmpNode赋值给node 就说明用右子树的最小值来代替待删除节点
        let tmpNode = this.getMinNode(node.right)
        //tmpNode赋值给待删除节点
        node.data = tmpNode.data
        //删除临时节点
        node.right = this._removeNode(node.right, tmpNode.data)
        return node
    } else if (data < node.data) {  //待删除节点在左子树上
        node.left = this._removeNode(node.left, data)
        return node
    } else { //待删除节点在右子树上
        node.right = this._removeNode(node.right, data)
        return node
    }
}
//删除节点
remove (data)
{
    this.root = this._removeNode(this.root, data);
}
  • 代码接收两个参数:data表示待删除的节点的值,node表示当前递归调用的节点。
  • 如果待删除节点的值等于当前节点的值(data == node.data),则进入条件判断。
  • 如果当前节点是叶子节点(即没有左子节点和右子节点),则将其置为null,表示删除该节点。
  • 如果当前节点只有左子节点而没有右子节点,则返回其左子节点,将其作为当前节点的父节点的新子节点。
  • 如果当前节点只有右子节点而没有左子节点,则返回其右子节点,将其作为当前节点的父节点的新子节点。
  • 如果当前节点既有左子节点又有右子节点,则需要找到待删除节点的右子树上的最小值来替代待删除节点。
  • 通过调用getMinNode(node.right)方法,找到右子树上的最小值所在的节点,并将其赋值给临时节点tmpNode
  • 将临时节点tmpNode的值复制到待删除节点node,相当于用右子树上的最小值替代了待删除节点。
  • 再次递归调用_removeNode()方法,传入当前节点的右子节点和临时节点的值,以删除右子树上的最小值节点。
  • 最后,返回当前节点node,表示删除操作完成。
  • 如果待删除节点的值小于当前节点的值(data < node.data),则递归调用_removeNode()方法,传入当前节点的左子节点和待删除节点的值,以在左子树上继续删除操作。
  • 如果待删除节点的值大于当前节点的值,则递归调用_removeNode()方法,传入当前节点的右子节点和待删除节点的值,以在右子树上继续删除操作。
  • 最终,整个删除操作完成后,返回当前节点node,并将其作为父节点的新子节点。

三:测试数据

    let myTree = new BSTree();

    myTree.insert(20);
    myTree.insert(13);
    myTree.insert(7);
    myTree.insert(9);
    myTree.insert(15);
    myTree.insert(14);
    myTree.insert(42);
    myTree.insert(22);
    myTree.insert(21);
    myTree.insert(24);
    myTree.insert(57);

image-20230718105531956

    console.log(myTree.getMaxNode());  
    console.log(myTree.getMinNode()); 
    myTree.remove(7)
    console.log(myTree.find(7));

image-20230718105624045

四:全部代码

    //二叉树的存储结构为
    class Node
    {
      constructor(data, left, right)
      {
        this.data = data
        this.left = left
        this.right = right
        //若有相同的元素插入节点,就放弃插入,count++
        this.count = 1
      }
    }

    //二叉排序树
    class BSTree
    {
      constructor()
      {
        this.root = null
      }

      //向二叉树插入节点
      insert (data)
      {
        let newNode = new Node(data, null, null)
        //更新根节点的值
        if (this.root === null) {
          this.root = newNode
        } else {
          //更新当前节点的值
          let currentNode = this.root
          //父节点就是空
          let parentNode = null
          while (true) {
            //更新父节点
            parentNode = currentNode
            //判断插入节点值在左子树还是右子树
            if (newNode.data < currentNode.data) {
              //更新当前节点
              currentNode = currentNode.left
              if (!currentNode) {
                parentNode.left = newNode
                break
              }
            }

            else if (newNode.data > currentNode.data) {
              currentNode = currentNode.right
              if (!currentNode) {
                parentNode.right = newNode
                break
              }
            }

            else if (newNode.data === currentNode.data) {
              //如果相同数据 count++ 不做处理
              currentNode.count++
              break
            }
          }
        }
      }

      //获取最小值
      getMinNode (node = this.root)
      {
        let currentNode = node
        while (currentNode.left) {
          currentNode = currentNode.left
        }
        return currentNode
      }

      //获取最大值
      getMaxNode (node = this.root)
      {
        let currentNode = node
        while (currentNode.right) {
          currentNode = currentNode.right
        }
        return currentNode
      }

      //寻找给定数据的节点
      find (data)
      {
        let currentNode = this.root
        while (currentNode) {
          if (currentNode.data == data) {
            return currentNode
          } else if (currentNode.data < data) {
            currentNode = currentNode.right
          } else {
            currentNode = currentNode.left
          }
        }
        return null
      }
      //删除节点,实例中不应调用
      _removeNode (node, data)
      {
        if (node === null) {
          return null
        }
        //找到要删除的节点的了 
        if (data === node.data) {
          //分三种情况
          //1. 要删除的节点为叶子结点 
          if (node.left === null && node.right === null) {
            return null
          }

          //2. 没有左子节点的节点
          if (node.left === null) return node.right

          //   没有右子节点的节点
          if (node.right === null) return node.left


          //3.有两个子节点的节点
          //找到待删除节点的右子树的最小值赋值给临时节点tmpNode
          //将tmpNode赋值给node 就说明用右子树的最小值来代替待删除节点
          let tmpNode = this.getMinNode(node.right)
          //tmpNode赋值给待删除节点
          node.data = tmpNode.data
          //删除临时节点
          node.right = this._removeNode(node.right, tmpNode.data)
          return node
        } else if (data < node.data) {  //待删除节点在左子树上
          node.left = this._removeNode(node.left, data)
          return node
        } else { //待删除节点在右子树上
          node.right = this._removeNode(node.right, data)
          return node
        }
      }
      //删除节点
      remove (data)
      {
        this.root = this._removeNode(this.root, data);
      }
    }

    let myTree = new BSTree();

    myTree.insert(20);
    myTree.insert(13);
    myTree.insert(7);
    myTree.insert(9);
    myTree.insert(15);
    myTree.insert(14);
    myTree.insert(42);
    myTree.insert(22);
    myTree.insert(21);
    myTree.insert(24);
    myTree.insert(57);
    console.log(myTree);

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

Java Spring和Spring集成Mybatis

0目录 1.Spring 2.Spring集成Mybatis 1.Spring 特性 IOC&#xff1a;控制反转 AOP&#xff1a;面向切面 Spring组成部分 在SMM中起到的作用&#xff08;粘合剂&#xff09; Spring理念 OOP核心思想【万物皆对象】 Spring核心思想【万物皆Bean组件】 Spring优势 低侵入式 …

【决策树-鸢尾花分类】

决策树算法简介 决策树是一种基于树状结构的分类与回归算法。它通过对数据集进行递归分割&#xff0c;将样本划分为多个类别或者回归值。决策树算法的核心思想是通过构建树来对数据进行划分&#xff0c;从而实现对未知样本的预测。 决策树的构建过程 决策树的构建过程包括以…

vue计时器

//将秒转化为时分秒 const resultTime ref();const formateSeconds function (endTime) {let secondTime parseInt(endTime); //将传入的秒的值转化为Numberlet min 0; // 初始化分let h 0; // 初始化小时// let result "";if (secondTime > 60) {//如果秒数…

PLC绝对值指令ABS()

在C语言里,ABS()指令属于基础指令,博途PLC系统也有绝对值指令。对于S7-200SMART PLC则需要自行构造,下面给出SMART PLC的绝对值指令ABS()。 1、S7-SMART PLC绝对值指令 2、STL代码 SUBROUTINE_BLOCK ABS:SBR3 TITLE=ABS()函数 VAR_INPUT x:REAL; END_VAR VAR_OUTPUT y:RE…

Redis学习1--Redis简介与基础数据类型操作

1、什么是Redis? Remote Dictionary Server&#xff0c;远程词典服务器&#xff0c;是一个基于内存的键值型NoSQL数据库 特点&#xff1a; 键值&#xff08;key-value&#xff09;型&#xff0c;value支持多种不同数据结构&#xff0c;功能丰富单线程&#xff0c;每个命令具…

【T1】存货成本异常、数量为零金额不为零的处理方法。

【问题描述】 使用T1飞跃专业版的过程中&#xff0c; 由于业务问题或者是操作问题&#xff0c; 经常会遇到某个商品成本异常不准确&#xff0c; 或者是遇到数量为0金额不为0的情况&#xff0c;需要将其成本调为0。 但是T1软件没有出入库调整单&#xff0c;并且结账无法针对数量…

QTday4(鼠标事件和键盘事件/QT实现连接TCP协议)

笔记 #ifndef WIDGET_H #define WIDGET_H#include <QWidget> #include <QDebug> #include <QTcpServer>//服务器类 #include <QTcpSocket>//客户端类 #include <QMessageBox> #include <QList>//链表容器QT_BEGIN_NAMESPACE namespace Ui …

ES6基础知识五:你是怎么理解ES6新增Set、Map两种数据结构的?

如果要用一句来描述&#xff0c;我们可以说 Set是一种叫做集合的数据结构&#xff0c;Map是一种叫做字典的数据结构 什么是集合&#xff1f;什么又是字典&#xff1f; 集合 是由一堆无序的、相关联的&#xff0c;且不重复的内存结构【数学中称为元素】组成的组合 字典 是…

【复盘与分享】第十一届泰迪杯B题:产品订单的数据分析与需求预测

文章目录 题目第一问第二问2.1 数据预处理2.2 数据集分析2.2.1 训练集2.2.2 预测集 2.3 特征工程2.4 模型建立2.4.1 模型框架和评价指标2.4.2 模型建立2.4.3 误差分析和特征筛选2.4.4 新品模型 2.5 模型融合2.6 预测方法2.7 总结 结尾 距离比赛结束已经过去两个多月了。 整个过…

【Python入门系列】第十九篇:Python基于协同过滤推荐系统的实现

文章目录 前言一、协同过滤算法简介二、计算相似度三、Python实现简单的协同过滤推荐系统总结 前言 推荐系统是现代互联网平台中的重要组成部分&#xff0c;它可以根据用户的兴趣和行为&#xff0c;向其推荐个性化的内容。协同过滤是推荐系统中常用的一种方法&#xff0c;它基…

Flutter:flutter_local_notifications——消息推送的学习

前言 注&#xff1a; 刚开始学习&#xff0c;如果某些案例使用时遇到问题&#xff0c;可以自行百度、查看官方案例、官方github。 简介 Flutter Local Notifications是一个用于在Flutter应用程序中显示本地通知的插件。它提供了一个简单而强大的方法来在设备上发送通知&#…

PostgreSQL 查询json/jsonb是否存在某个片段

文章目录 前言实现实现思路坑1坑2坑3 恍然大悟 前言 在PostgreSQL中&#xff0c;jsonb有额外的操作符&#xff0c;如 >、<、?、?|、?& 可以用来查询是否包含路径/值&#xff0c;以及顶层键值是否存在。 详细文章&#xff1a;PostgreSQL 操作json/jsonb 那么&am…

青大数据结构【2021】

一、单选&#xff08;17&#xff01;&#xff09; 根据中序遍历得到降序序列可以知道&#xff0c;每个结点的左子树的结点的值比该结点的值小&#xff0c;因为没有重复的关键字&#xff0c;所以拥有最大值的结点没有左子树。 二、简答 三、分析计算 四、算法分析 3.迪杰斯特拉…

LLaMA2可商用|GPT-4变笨|【2023-0723】【第七期】

一、大咖观点&#xff1a; 傅盛&#xff1a;ChatGPT时代如何创业 - BOTAI - 博客园Google 已经被OpenAI 超越了吗&#xff1f;| AlphaGo 之父深度访谈《人民日报》&#xff1a;大模型的竞争&#xff0c;是国家科技战略的竞争WAIC 2023 | 张俊林&#xff1a;大语言模型带来的交…

贝塞尔曲线与B样条曲线

B-spline and Bezier Curve 介绍一下robotics运动规划方向的B样条曲线与贝塞尔曲线相关知识。 0728&#xff1a;TODO&#xff0c;节点向量如何得到&#xff1f; 贝塞尔曲线&#xff0c;B-样条&#xff0c;非均匀有理B样条梳理曲线篇: 贝塞尔曲线Animated Bzier CurvesBzier …

gin框架内容(三)--中间件

gin框架内容&#xff08;三&#xff09;--中间件 Gin框架允许开发者在处理请求的过程中&#xff0c;加入用户自己的函数。这个函数就叫中间件&#xff0c;中间件适合处理一些公共的业务逻辑&#xff0c;比如登录认证、权限校验、数据分页、记录日志、耗时统计等 即比如&#x…

【牛客面试必刷TOP101】Day1.反转链表和合并两个排序的链表

作者简介&#xff1a;大家好&#xff0c;我是未央&#xff1b; 博客首页&#xff1a;未央.303 系列专栏&#xff1a;牛客面试必刷TOP101 每日一句&#xff1a;人的一生&#xff0c;可以有所作为的时机只有一次&#xff0c;那就是现在&#xff01;&#xff01;&#xff01;&…

【Ansible】Ansible自动化运维工具的应用与常用命令

ansible自动化运维工具 一、ansible 的概述1. ansible 的概念2. ansible 的特性 二、ansible 的部署与命令1. ansible 的部署1.1 服务器ip地址设置1.2 ansible 服务器部署 2. ansible 命令行模块2.1 command 模块2.2 shell 模块2.3 cron 模块2.4 user 模块2.5 group 模块2.6 co…

财报解读:新鲜感褪去后,微软直面AI的骨感现实?

微软交出了一份远观尚可&#xff0c;但近看承压的“答卷”。 北京时间2023年7月26日&#xff0c;微软披露了2023财年第四财季及全年财报。受生产力和业务流程部门和智能云部门等业务带动&#xff0c;微软第四财季营收561.89亿美元&#xff0c;同比增长8%&#xff1b;净利润200…

【iOS】—— 持久化

文章目录 数据持久化的目的iOS中数据持久化方案数据持久化方式分类内存缓存磁盘缓存 沙盒机制获取应用程序的沙盒路径沙盒目录的获取方式 持久化数据存储方式XML属性列表Preferences偏好设置&#xff08;UserDefaults&#xff09;数据库存储什么是序列化和反序列化&#xff0c;…
最新文章