数据结构: 二叉搜索树

目录

1.二叉搜索树概念

2.二叉搜索树的操作

3.二叉搜索树的实现

3.1定义BST

3.2功能实现

1.默认成员函数

2.非递归

插入

查找

删除

3.递归

插入

查找

删除

4.二叉搜索树的应用


1.二叉搜索树概念

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

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

2.二叉搜索树的操作

1.查找

从根开始查找:

--若查找的值比根节点的值大, 向右查找. 

--若比根节点的值小向左查找.

--循环下去,若到nullptr都没找到,返回false

2.插入

a.空树

1.直接将其设为根节点

b.非空树

1.根据二叉搜索树的性质,找到插入的位置

2.在该位置上新生成一个节点,并与父节点链接

(可能是左,也可能是右) 根据这个节点的key与parent的key值判断在那边链接

3.删除

1.找到要删除的节点

2.对该节点进行讨论:

a.该节点没有节点或只有1子个节点

--可以直接删除, 并将它的子节点给它的父点

b.该节点有2个子节点

--找到它左子树最大节点/右子树最小节点来代替它, 并将其删除

3.细节处理

当删除到root->left/right为空的时候, 需要更新头节点

3.二叉搜索树的实现

3.1定义BST

节点

template<class K >
struct BSTreeNode 
{BSTreeNode* _left;BSTreeNode* _right;K _key;//构造函数BSTreeNode(const K& k) :_left(nullptr),_right(nullptr),_key(k){}
};

BSTree

template<class K, class V>
class BSTree
{typedef BSTreeNode<K, V> Node;
public://功能bool Insert(const K& key, const V& value);Node* Find(const K& key);bool Erase(const K& key);void _InOrder(Node* root);void InOrder();private:Node* _root = nullptr;
};

3.2功能实现

1.默认成员函数

构造函数

	//构造函数BSTree():_root(nullptr){}

拷贝构造

	//拷贝构造BSTree(const BSTree<K>& t) {Copy(t._root);}Node* Copy(Node* root){if (root == nullptr)return nullptr;//前序遍历拷贝Node* newRoot = new Node(root->_key);newRoot->_left = Copy(root->_left);newRoot->_right = Copy(root->_right);return newRoot;}

赋值运算符

	//赋值运算符BSTree<K>& operator=(BSTree<K> t){swap(_root,t._root);return *this;}

析构函数

	//析构函数~BSTree() {Destory(_root);}void Destory(Node*& root) {if (root == nullptr)return;Destory(root->_left);Destory(root->_right);delete root;root = nullptr;}

2.非递归

插入

a.空树

--插入的节点直接变为根节点

b.非空树

--根据二叉搜索树性质找到插入的位置

--与父节点链接(讨论parent的key与插入节点的key值来决定链接在parent的那边)

	bool Insert(const K& key){//a.空树(直接将该节点设为根节点)if (_root == nullptr){_root = new Node(key);return true;}//b.非空树//1.找到插入的位置Node* cur = _root;Node* parent = nullptr;while (cur){if (key < cur->_key){parent = cur;cur = cur->_left;}else if (key > cur->_key){parent = cur;cur = cur->_right;}else{return false;}}//2.链接节点cur = new Node(key);if (parent->_key > key){parent->_left = cur;}else{parent->_right = cur;}return true;}

查找

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

删除

找到删除节点的位置

1.删除的节点只有1个节点或者没有节点(直接删除,将cur的子节点给parent)

--没有节点也可以这么操作,相当于把空的子节点给父亲

--若有1个子节点,把该子节点给父亲 (讨论cur的位置,可能是p的左也可能是p的右)

2.删除的节点有2个节点:  找到它左子树最大的节点/ 右子树最小的节点代替它, 并删除

--找到删除的节点

--找到左子树最大节点/右子树最小节点来代替它(把cur存的key变为leftMax/rightMin的key)

--找该节点的过程, cur是在leftMax/rightMin的位置,删除cur节点

(当然要处理它的子节点,讨论一下leftMax是pleftMax左边还是右边)

3.处理空指针: 当删除的节点为cur, 并且cur只有左子树或者只有右子树,更新头节点

	bool Erase(const K& key) {//找到要删除的节点Node* parent = nullptr;Node* cur = _root;while (cur) {if (cur->_key > key) {parent = cur;cur = cur->_left;}else if (cur->_key < key){parent = cur;cur = cur->_right;}else {//删除//1.左为空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;}//2.右为空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;}}}//3.左右不为空(找左子树最大节点/右子树最小节点)else {Node* pmaxLeft = cur;Node* maxLeft = cur->_left;while (maxLeft->_right) {pmaxLeft = maxLeft;maxLeft = maxLeft->_right;}cur->_key = maxLeft->_key;//把maxLeft的子节点交给其parent,然后删除maxLeftif (pmaxLeft->_left == maxLeft) {pmaxLeft->_left = maxLeft->_left;}else {pmaxLeft->_right = maxLeft->_left;}delete maxLeft;}return true;}}return false;}

3.递归

插入

	//1.插入bool _InsertR(Node*& root, const K& key){if (root == nullptr){root = new Node(key);return true;}if (root->_key > key){return _InsertR(root->_left, key);}else if (root->_key < key){return _InsertR(root->_right, key);}}

查找

根据二叉搜索树的性质查找:

	//2.查找bool _FindR(Node*& root, const K& key){if (root == nullptr){return false;}if (root->_key == key)return true;if (root->_key > key)return _FindR(root->_left, key);elsereturn _FindR(root->_right, key);}

删除

	//3.删除bool _EraseR(Node*& root, const K& key) {if (root == nullptr)return false;//找到要删除的节点if (root->_key > key) {return _EraseR(root->_left, key);}else if (root->_key < key) {return _EraseR(root->_right, key);}else {//删除Node* del = root;//1.左为空if (root->_left == nullptr) {root = root->_right;}//2.右为空else if (root->_right == nullptr) {root = root->_left;}//3.左右不为空else {Node* minRight = root->_right;while (minRight->_left) {minRight = minRight->_left;}swap(root->_key,minRight->_key );//转换为在它的右子树去删除return _EraseR(root->_right,key);}delete del;return true;}}

效果:

4.二叉搜索树的应用

1. K模型:K模型即只有key作为关键码,结构中只需要存储Key即可,关键码即为需要搜索到的值。
比如:给一个单词word,判断该单词是否拼写正确,具体方式如下:

  • 以词库中所有单词集合中的每个单词作为key,构建一棵二叉搜索树
  • 在二叉搜索树中检索该单词是否存在,存在则拼写正确,不存在则拼写错误。

2. KV模型:每一个关键码key,都有与之对应的值Value,即<Key, Value>的键值对。该种方
式在现实生活中非常常见:

  • 比如英汉词典就是英文与中文的对应关系,通过英文可以快速找到与其对应的中文,英文单词与其对应的中文<word, chinese>就构成一种键值对;
  • 再比如统计单词次数,统计成功后,给定单词就可快速找到其出现的次数,单词与其出现次数就是<word, count>就构成一种键值对

示例

英汉词典

统计次数

中序遍历打印的时候,补上value就行:

达到上面的效果:把K改为KV型

节点:

BSTree

5.性能分析

最优: logN

最差: N

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

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

深拷贝和浅拷贝

一、深浅拷贝是什么&#xff1f; 首先我们要明白一点&#xff0c;js中数据类型分为&#xff1a; 基本数据类型: Number, String, Boolean, Null, Undefined, Symbol 引用数据类型: Object &#xff0c;Array , Function 对于引用数据 类型才有深浅拷贝的说法 1. 浅拷贝&…

中国移动集采120万部,助推国产5G赶超iPhone15

近期媒体纷纷传出消息指中国移动将大规模集采&#xff0c;预计将采购国产5G手机120万台&#xff0c;加上另外两家运营商的集采数量&#xff0c;估计集采数量可能达到300万部&#xff0c;如此将有助于它在国内高端手机市场赶超苹果。 国产5G手机在8月底突然上市&#xff0c;获益…

科技与时尚共进化,优衣库以硬实力创造品牌长期价值

时尚总是轮回&#xff0c;服装产品如何保持长青&#xff1f;对优衣库来说&#xff0c;产品力不褪色的密码之一&#xff0c;就是始终坚持推动服装科技与时尚融合&#xff0c;赋予生活潮流更多内涵&#xff0c;和更高品质的穿搭体验。 这一点&#xff0c;往往在每年换季新品上市…

Ubuntu docker安装mysql

本文介绍如何在docker中安装mysql&#xff0c;之前有尝试过先在docker中安装一个ubuntu到镜像&#xff0c;然后进去再去安装mysql相关的东西&#xff0c;发现不行&#xff0c;这边整理一下一个可行的方式。 在下载镜像的时候&#xff0c;直接下载mysql镜像。 1.搜索镜像 doc…

如何用滚动字幕丰富视频内容?只需5个步骤

视频制作需要各种元素来吸引观众的注意力&#xff0c;其中滚动字幕是一个非常实用的工具&#xff0c;可以提供背景信息&#xff0c;引导观众&#xff0c;或者作为一种装饰元素。以下是如何使用滚动字幕丰富视频内容的五个步骤&#xff1a; 1. 下载并安装适合的辅助工具 为了快速…

修炼k8s+flink+hdfs+dlink(六:学习k8s)

一&#xff1a;增&#xff08;创建&#xff09;。 直接进行创建。 kubectl run nginx --imagenginx使用yaml清单方式进行创建。 二&#xff1a;删除。 kubectl delete pods/nginx 三&#xff1a;修改。 kubectl exec -it my-nginx – /bin/bash 四&#xff1a;查看。 …

保序回归与金融时序数据

保序回归在回归问题中的作用是通过拟合一个单调递增或递减的函数&#xff0c;来保持数据点的相对顺序特性。 一、保序回归的作用 主要用于以下情况&#xff1a; 1. 有序数据&#xff1a;当输入数据具有特定的顺序关系时&#xff0c;保序回归可以帮助保持这种顺序关系。例如&…

Linux文件管理与用户管理

一、查看文件内容 1、回顾之前的命令 cat命令、tac命令、head命令、tail命令、扩展&#xff1a;tail -f动态查看一个文件的内容 2、more分屏显示文件内容&#xff08;了解&#xff09; 基本语法&#xff1a; # more 文件名称 特别注意&#xff1a;more命令在加载文件时并不…

PTA 小字辈(树)

题目 本题给定一个庞大家族的家谱&#xff0c;要请你给出最小一辈的名单。 输入格式&#xff1a; 输入在第一行给出家族人口总数 N&#xff08;不超过 100 000 的正整数&#xff09; —— 简单起见&#xff0c;我们把家族成员从 1 到 N 编号。随后第二行给出 N 个编号&#…

实时配送跟踪功能的实现:外卖跑腿小程序的技术挑战

在当今数字化时代&#xff0c;外卖和跑腿服务已经成为了生活中不可或缺的一部分。为了提供更好的用户体验&#xff0c;外卖跑腿小程序越来越注重实时配送跟踪功能的实现。这项技术挑战旨在确保顾客可以方便地跟踪他们的订单&#xff0c;以及配送员可以高效地完成送货任务。本文…

面向对象设计原则之依赖倒置原则

目录 定义原始定义进一步的理解 作用实现方法代码示例 面向对象设计原则之开-闭原则 面向对象设计原则之里式替换原则 面向对象设计原则之依赖倒置原则 面向对象设计原则之单一职责原则 定义 依赖倒置原则&#xff08;Dependence Inversion Principle&#xff09;&#xff0c…

信息系统项目管理师第四版学习笔记——组织通用管理

组织通用管理是项目管理的关键前提和基础&#xff0c;它为项目管理提供思想路线和基本原则与方法&#xff0c;项目管理则是通用管理方法在特定场景下的具体表现。 人力资源管理 人力资源管理基础 人力资源管理的广义目标是充分利用组织中的人员使组织的各项工作效率水平达到…