当前位置: 首页 > news >正文

二叉搜索树的模拟实现

一,引言

为了实现更加快速的搜索数据,以及不局限于数组形式的存储数据,引入二叉搜索树这一数据结构。二叉搜索树有以下特点:

1,首先二叉搜索树是一颗二叉树

2,若它的左⼦树不为空,则左⼦树上所有结点的值都⼩于等于根结点的值。

3,若它的右⼦树不为空,则右⼦树上所有结点的值都⼤于等于根结点的值。

4,它的左右⼦树也分别为⼆叉搜索树。

举个例子如下:

如上图:每一棵左子树的值都小于等于根节点。每一棵右子树的值都大于等于根节点。每一棵子树也满足上述规则。

二,时间复杂度分析

最优情况下,⼆叉搜索树为完全⼆叉树(或者接近完全⼆叉树),其⾼度为 log2^N

最差情况下,⼆叉搜索树退化为单⽀树(或者类似单⽀),其⾼度为:N

所以综合⽽⾔⼆叉搜索树增删查改时间复杂度为:O(N)

虽然在O(N)的时间复杂度在查找算法上并不占优势,但是二叉搜索树的插入规则为以后的AVLTree以及红黑树做准备。

如上图:在这种特殊的二叉搜索树的时间复杂度就近似于O(N) 。

三,二叉搜索树的插入

在上述了解了二叉搜索树的实现规则之后,在实现二叉搜索树的插入之前要先讲解一下,每一个二叉搜索树的单个节点

在二叉搜索树中每一个节点由四部分组成:关键字(key)变量--负责在二叉搜索树中进行比较,并且不能进行修改(value)每一个关键字(key)对应一个value ,value是可以进行修改,并且不参与比较逻辑,指向左孩子的指针,指向右孩子的指针。四个部分如下:

template<class K, class V>struct BSTNode{K _key;V _value;BSTNode<K, V>* _left;BSTNode<K, V>* _right;BSTNode(const K& key, const V& value):_key(key), _value(value), _left(nullptr), _right(nullptr){}};

BSTNode对该对象实现初始化操作,走初始化列表

实现单个节点的创建之后。开始实现二叉搜索树的插入,首先创建二叉搜索树的大框架模型如下:

template<class K, class V>class BSTree{//typedef BSTNode<K> Node;using Node = BSTNode<K, V>;public:// 强制生成构造BSTree() = default;
private:Node* _root = nullptr;};
}

开始插入节点,若是首次插入节点,则直接将根节点(_root)指向该新节点;若不是新插入的节点,则从根节点开始比较,插入的key大于根节点的key,指向该根节点的指针向右到右孩子的位置,若插入的key小于根节点的key,指向该节点的指针向左到左孩子的位置,若两者的key相等则直接返回false。代码如下:

bool Insert(const K& key){if (_root == nullptr){_root = new Node(key);return true;}Node* parent = nullptr;Node* cur = _root;while (cur){if (cur->_key < key){parent = cur;cur = cur->_right;}else if (cur->_key > key){parent = cur;cur = cur->_left;}else{return false;}}cur = new Node(key);if (parent->_key < key){parent->_right = cur;}else{parent->_left = cur;}return true;}

这里parent指针指向cur的父类节点,当cur为nullptr,该节点指向new出来新的节点。此时需要cur的父类节点(parent)。在cur指向new的节点之前,需要先进行标记父类节点,以防止找不到父类节点。

四,二叉搜索树的查找

查找规则和插入规则相似,从根节点(_root)开始一步步进行判断,若大于key的值,则根节点向右到右孩子节点。一步步进行判断,若key相等,则返回true。如果到nullptr节点依然没有找到相对应的key,则返回false 。代码如下:

bool Find(const K& key){Node* cur = _root;while (cur){if (cur->_key < key){cur = cur->_right;}else if (cur->_key > key){cur = cur->_left;}else{return true;}}return false;}

五,删除关键字为(key)的节点

删除节点和插入节点不同。删除节点非为一下三种情况:
1,要删除的左右节点同时都为空如图:

如图当删除1这个节点,只需要将该节点delete之后,父类位置对应的向下节点置空。

2,要删除的左右节点有一个为空,如上图的10,把删除结点的⽗亲对应孩⼦指针指向删除节点的左孩子或右孩⼦,直接删除需要删除的结点。如图:

3,删除节点的两个孩子都有节点。如图:

这时无法直接删除对应位置的节点,需要用到替换法进行删除:找N左⼦树的值最⼤结点 R(最右结点)或者N右⼦树的值最⼩结点R(最左结点)替代N,因为这两个结点中任意⼀个,放到N的 位置,都满⾜⼆叉搜索树的规则。替代N的意思就是N和R的两个结点的值交换,转⽽变成删除R结 点,R结点符合情况1或情况2,可以直接删除。如上图可以将3替换成(左子树的最大节点1  ;右子树的最小节点4);将8替换成(左子树的最大节点7   ;右子树的最小节点10)。

代码实现如下:

bool Erase(const K& key){Node* parent = nullptr;Node* cur = _root;while (cur){if (cur->_key < key){parent = cur;cur = cur->_right;}else if (cur->_key > key){parent = cur;cur = cur->_left;}else{// 删除// 左为空if (cur->_left == nullptr){if (cur == _root){_root = cur->_right;}else{if (parent->_left == cur){parent->_left = cur->_right;}else{parent->_right = cur->_right;}}delete cur;}else if (cur->_right == nullptr){if (cur == _root){_root = cur->_left;}else{// 右为空if (parent->_left == cur){parent->_left = cur->_left;}else{parent->_right = cur->_left;}}delete cur;}else{// 左右都不为空// 右子树最左节点Node* replaceParent = cur;Node* replace = cur->_right;while (replace->_left){replaceParent = replace;replace = replace->_left;}cur->_key = replace->_key;if (replaceParent->_left == replace)replaceParent->_left = replace->_right;elsereplaceParent->_right = replace->_right;delete replace;}return true;}}return false;}

第一步找到删除的位置,第二步判断该删除节点的位置输入上述哪种情况,前两种情况需要又分为删除的节点是否为根节点,这两种需要单独进行编写。第三种情况如上代码样例,需要先找右子树的最左节点。找到之后进行key的替换,最后删除右子树的最左节点。这里的删除需要注意:需要一个该节点的父类节点进行标记,逻辑和插入节点类似。

六,总结

二叉搜索树总体的逻辑不难理解,比较复杂的是删除操作。需要注意的是由于单个节点并没有设置指向父类的指针,因此尤其注意在删除和插入对父类节点的标记。


http://www.dtcms.com/a/335864.html

相关文章:

  • 【MySQL学习|黑马笔记|Day7】触发器和锁(全局锁、表级锁、行级锁、)
  • Golang 后台技术面试套题 1
  • 天地图应用篇:增加全屏、图层选择功能
  • 2023年全国研究生数学建模竞赛华为杯E题出血性脑卒中临床智能诊疗建模求解全过程文档及程序
  • multiboot 规范实践分析
  • STM32—OTA-YModem
  • Linux设备模型深度解析
  • RISC-V汇编新手入门
  • Java项目中短信的发送
  • 判断回文数的两种高效方法(附Python实现)
  • Webflux核心概念、适用场景分析【AI Chat类项目选型优势】
  • 数据链路层(2)
  • MySQL的事务基础概念:
  • 显式编程(Explicit Programming)
  • 深入解析函数指针及其数组、typedef关键字应用技巧
  • Go面试题及详细答案120题(21-40)
  • Pycharm Debug详解
  • C++ vector的使用
  • 自动驾驶中的传感器技术34——Lidar(9)
  • 前端项目练习-王者荣耀竞赛可视化大屏 -Vue纯前端静态页面项目
  • Springboot项目3种视图(JSP、Thymeleaf、Freemarker)演示
  • 图解直接插入排序C语言实现
  • 3.逻辑回归:从分类到正则化
  • pyecharts可视化图表组合组件_Grid:打造专业数据仪表盘
  • 矿物分类案列 (一)六种方法对数据的填充
  • C#WPF实战出真汁13--【营业查询】
  • 《设计模式》工厂方法模式
  • 数据结构与算法之 leetcode 98. 验证二叉搜索树 (前序,中序,后序遍历)
  • 影刀 RAP 迁移华为云备忘录数据到得到笔记
  • GitHub Copilot:AI编程助手的架构演进与真实世界影响