[C++][数据结构]二叉搜索树:介绍和实现

devtools/2024/10/22 16:19:14/

二叉搜索树

概念

二叉搜索树又称二叉排序树,它是一棵空树,或者是具有以下性质的二叉树:

  • 若它的左子树不为空,则左子树上所有节点的值都小于根节点的值
  • 若它的右子树不为空,则右子树上所有节点的值都大于根节点的值
  • 它的左右子树也分别为二叉搜索树

模拟实现(非递归)

节点

	template<class K, class V>struct BSTreeNode	//节点{using Node = BSTreeNode<K, V>;Node* _left;	//左节点Node* _right;	//右节点K _key;			//键V _value;		//值BSTreeNode(const K& key, const V& value):_left(nullptr), _right(nullptr), _key(key),_value(value){}				//拷贝构造};

寻找

根据BSTree的特点,

  • 比该节点的值大,就走到该节点右节点
  • 比该节点的值小,就走到该节点左节点

插入

  1. 先寻找,有相同值则return false
  2. 插入,链接父节点和原来的子树

删除

没有孩子/一个孩子

对于这种情况,

  • 当删除节点左边为空,就将该节点右边托付给父亲
  • 右边为空,就将左边托付给父亲
    所以对于删除叶子节点也可以归纳到这里面

两个孩子:替换法

找一个能替换删除节点的节点,交换值,转换删除替换节点
比如:删除图中3这个节点
我们可以将左子树的最大节点或者右子树的最左节点替换掉3
即左子树最右节点或者右子树最左节点

在这里插入图片描述

rightMin的问题

假如rightMinParent为空指针,那下面这句特殊情况会出错

rightMinParent->_left = rightMin->_right;

在这里插入图片描述

这幅图中,当删除根节点时,右子树最左节点为空,不进循环,rightMinParent为空,访问空节点的左子树会出现错误

代码

测试代码

void TestBSTree()
{BSTree<string, string> dict;dict.Insert("insert", "插入");dict.Insert("erase", "删除");dict.Insert("left", "左边");dict.Insert("string", "字符串");string str;while (cin >> str){auto ret = dict.Find(str);if (ret){cout << str << ":" << ret->_value << endl;}else{cout << "单词拼写错误" << endl;}}string strs[] = { "苹果", "西瓜", "苹果", "樱桃", "苹果", "樱桃", "苹果", "樱桃", "苹果" };// 统计水果出现的次BSTree<string, int> countTree;for (auto str : strs){auto ret = countTree.Find(str);if (ret == NULL){countTree.Insert(str, 1);}else{ret->_value++;}}countTree.InOrder();
}

完整代码

namespace key_value
{template<class K, class V>struct BSTreeNode	//节点{using Node = BSTreeNode<K, V>;Node* _left;	//左节点Node* _right;	//右节点K _key;			//键V _value;		//值BSTreeNode(const K& key, const V& value):_left(nullptr), _right(nullptr), _key(key),_value(value){}				//拷贝构造};template<class K, class V>class BSTree{using Node = BSTreeNode<K, V>;public://插入bool Insert(const K& key, const V& value){if (_root == nullptr){_root = new Node(key, value);return true;}//如果树为空,特殊判断Node* parent = nullptr;//父节点//方便记录父节点原来的子树Node* cur = _root;while (cur != nullptr){if (cur->_key > key){parent = cur;cur = cur->_left;}else if (cur->_key < key){parent = cur;cur = cur->_right;}else{return false;}}//查找完再判断是父节点的左树还是右数//标记为Acur = new Node(key, value);if (parent->_key > key){parent->_right = cur;}else{parent->_right = cur;}return true;}//查找,并返回这个节点的指针(位置)Node* Find(const K& key){Node* cur = _root;while (cur != nullptr){if (cur->_key > key){cur = cur->_right;}else if (cur->_key < key){cur = cur->_left;}else{return cur;}}return nullptr;}//删除节点bool Erase(const K& key){//并没有在开头判断删除节点是否为根节点//而是在下面Node* parent = nullptr;Node* cur = _root;while (cur != nullptr){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{	//功能Aif (cur == parent->_right){parent->_right = cur->_right;}else{parent->_left = cur->_right;}}delete cur;return true;}else if (cur->_right == nullptr){if (cur == _root){_root = cur->left;}else{	//功能Aif (cur == parent->_right){parent->_right = cur->_left;}else{parent->_left = cur->_left;}}delete cur;return true;}else{// 采用右树最左节点替换法Node* rightMin = cur->_right;Node* rightMinParent = cur;while (rightMin->_left){rightMinParent = rightMin;rightMin = rightMin->_left;}cur->_key = rightMin->_key;if (rightMin == rightMinParent->_right){rightMinParent->_right = rightMin->_right;}else{rightMinParent->_left = rightMin->_left;}delete rightMin;return true;}}}return false;}void InOrder()//中序打印{_InOrder(_root);}//因为外部取不到_root//所以再套一层private:Node* _root = nullptr;void _InOrder(Node* root){if (root == nullptr){return;}_InOrder(root->_left);std::cout << root->_key << " ";_InOrder(root->_right);}};
}

结语

现在再来写BSTree感觉也不是很困难,希望对你有帮助,我们下次见
(补充一下,代码是不全的,BSTree() 和~BSTree()都没写,用的默认的)


http://www.ppmy.cn/devtools/30655.html

相关文章

笔记-用Python脚本启停JAR程序

用Python脚本启停JAR程序&#xff0c;需要用到python中的以下内置模块 subprocess 是 Python 的一个标准库模块&#xff0c;用于在新进程中执行子命令&#xff0c;获取子进程的输入/输出/错误以及返回码等os 是 Python 的一个标准库模块&#xff0c;它提供了与操作系统交互的功…

设计模式: 模板模式

目录 一&#xff0c;模板模式 二&#xff0c;特点 三&#xff0c;组成部分 四&#xff0c;实现步骤 五&#xff0c;案例 一&#xff0c;模板模式 模板模式&#xff08;Template Pattern&#xff09;是一种行为型设计模式&#xff0c;它在超类中定义了一个算法的骨架&#…

C++基础—模版

C模板是C语言中实现泛型编程的核心机制&#xff0c;它允许程序员定义通用的代码框架&#xff0c;这些框架在编译时可以根据提供的具体类型参数生成相应的特定类型实例。 泛型编程的特点代码复用和安全性! 模板主要分为两大类&#xff1a;函数模板和类模板。 函数模板 基本语…

SpringBoot-@Transactional注解失效

Transactional注解失效 Transactional失效场景 以下是一些常见导致Transactional注解失效的场景&#xff0c;配合相应的Java代码演示&#xff1a; 1、方法修饰符非公开&#xff08;非public&#xff09; Transactional注解失效的原因在于Spring事务管理器如何实现对事务的代…

springboot2.6.7集成springfox3.0.0

springboot2.6.7集成springfox3.0.0 1. pom配置2. 增加swagger自动配置类3. 配置修改4. 自动配置类增加以下内容参考 1. pom配置 <!-- https://mvnrepository.com/artifact/io.springfox/springfox-swagger-ui --> <dependency><groupId>io.springfox</g…

机器学习入门之非监督学习和半监督学习

文章目录 非监督学习半监督学习机器学习的核心价值 非监督学习 与监督学习相反&#xff0c;非监督学习的训练数据集是完全没有标签的数据&#xff0c;他本质上所做的工作都是聚类的 给定数据之后&#xff0c;聚类能从中学习到什么&#xff0c;就完全取决于数据本身的特性的&a…

上海计算机学会2022年4月月赛C++丙组T3平衡括号(简)

题目描述 给定一个只包含 ( 与 ) 的括号序列&#xff0c;请删除尽量少的括号&#xff0c;使它变成平衡的。平衡的定义如下&#xff1a; 空序列是平衡的&#xff1b;如果某个括号序列 s 是平衡的&#xff0c;那么 (s) 也是平衡的&#xff1b;如果某两个括号序列 s 与 t 都是平…

【机器学习】机器学习学习笔记 - 监督学习 - KNN线性回归岭回归 - 02

监督学习 KNN (k-nearest neighbors) KNN 是用 k 个最近邻的训练数据集来寻找未知对象分类的一种算法 from sklearn import neighbors# 分类 # 创建KNN分类器模型并进行训练 classifier neighbors.KNeighborsClassifier(num_neighbors, weightsdistance) classifier.fit(X,…