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

【数据结构】二叉搜索树的递归与非递归实现

1. 二叉搜索树

二叉搜索树(Binary Search Tree,简称 BST),也叫做二叉排序树,是一种常用于查找的数据结构。

它具有以下特点:

1. 每个节点最多有两个子节点,分别称为左子节点和右子节点。

2. 若左子树不为空,左子树中的所有节点的值都小于根节点的值。

3. 若右子树不为空,右子树中的所有节点的值都大于根节点的值。

4. 左右子树也都是二叉搜索树。

需要特别注意的是空树也是一棵二叉搜索树。由于其性质,二叉搜索树在进行中序遍历时就能对数据进行升序排序。

这种结构使其进行查找、插入和删除操作具有较高的效率。例如,如果要查找一个特定的值,从根节点开始,如果要查找的值小于根节点的值,就向左子树查找;如果大于根节点的值,就向右子树查找。一直重复这个过程,直到找到目标值或者确定目标值不存在。

在插入操作时,也是通过比较要插入的值与当前节点的值,决定将其插入到左子树还是右子树。

删除操作则相对复杂一些,需要根据被删除节点的子节点情况进行不同的处理。

并且二叉搜索树在很多领域都有广泛应用,比如数据库中的索引结构、文件系统的目录结构等。它能够有效地提高数据的存储和检索效率。

2. 二叉搜索树的结构

2.1 二叉搜索树的节点

二叉搜索树的节点本质与二叉树一样,所以有三个成员变量:左子树_left,右子树_right,键值_val。当然为了适配不同的类型,我们可以定义一个模版.。

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

2.2 二叉搜索树

我们在定义二叉搜索树时就可以利用这个节点,并初始化为空。

	template<class K>class BSTree{typedef BSTreeNode<K> Node;public:private:Node* _root = nullptr;};

3. 二叉搜索树的初始化与销毁

3.1 构造函数 / 拷贝构造 / 赋值重载

首先我们直接定义一个无参的构造函数,因为我们在定义拷贝构造之后编译器就不会再生成默认的构造函数了。

		BSTree() {}

之后我们可以利用递归实现一个拷贝构造。

		BSTree(const BSTree& t){_root = copy(t._root);}Node* copy(Node* root){if (root == nullptr)return nullptr;Node* newnode = new Node(root->_key);newnode->_left = copy(root->_left);newnode->_right = copy(root->_right);return newnode;}

最后我们通过一个简单的方式实现赋值重载——通过形参调用拷贝构造出一个临时变量,然后交换 this 所指向的变量,这样原本 this 所指向的对象出了作用域就会销毁,间接实现了实现赋值重载

		BSTree<K>& operator=(const BSTree<K> t){swap(_root, t._root);return *this;}

3.2 析构函数

析构函数需要借助递归释放所有节点,而为了方便我们传参我们可以定义子函数来帮助我们解决。

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

4. 二叉搜索树的插入

二叉搜索树的插入十分简单,从根节点开始,如果要查找的值小于根节点的值,就向左子树查找;如果大于根节点的值,就向右子树查找,一直重复这个过程。

在查找过程中可能出现两种情况:

1. 查找到空节点要插入位置,插入成功返回 true。

2. 查找到相同值的节点,插入失败返回 false。

4.1 非递归实现

非递归实现首先得保存父节点 parent,方便之后插入新的节点。然后循环查找插入位置,查找失败就返回 false,查找成功则判断该插入 parent 的左边还是右边。

		bool Insert(const K& key){if (_root == nullptr){_root = new Node(key);return true;}Node* parent = nullptr;Node* cur = _root;while (cur){if (key < cur->_key){parent = cur;cur = cur->_left;}else if (key > cur->_key){parent = cur;cur = cur->_right;}else{return false;}}//插入cur = new Node(key);//判断该插入哪边if (key < parent->_key){parent->_left = cur;}else{parent->_right = cur;}return true;}

4.2 递归实现

递归实现的逻辑与非递归实现时一样的,当 key < root->_key 就往左子树插入,当 key > root->_key 就往右子树插入。其中需要注意的是为了传参方便我们定义一个子函数来实现递归,并且利用引用传参减少拷贝并直接对原二叉搜索树进行修改。

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

5. 二叉搜索树的查找

二叉搜索树的查找也十分简单,从根节点开始,如果要查找的值小于根节点的值,就向左子树查找;如果大于根节点的值,就向右子树查找,一直重复这个过程。

在查找过程中可能出现两种情况:

1. 查找到空节点,返回 false。

2. 查找到相同值的节点,返回 true。

5.1 非递归实现

		bool 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 true;}}return false;}

5.2 递归实现

		bool FindR(const K& key){return _FindR(_root, key);}bool _FindR(Node* root, const K& key){if (root == nullptr){return false;}if (key < root->_key){return _FindR(root->_left, key);}else if (key > root->_key){return _FindR(root->_right, key);}else{return true;}}

6. 二叉搜索树的删除

二叉搜索树的删除的逻辑是最麻烦的,首先我们定义一个 parent 节点方便之后链接。然后通过具体情况分析:

主要可以分为以下几种情况:

1. 删除节点的左子树为空,位于父节点的右子树

2. 删除节点的左子树为空,位于父节点的左子树

3. 删除节点的右子树为空,位于父节点的右子树

4. 删除节点的右子树为空,位于父节点的左子树

5. 删除节点为根节点,并且左或右子树为空

6. 删除节点左右子树都不为空

当删除节点左右子树都不为空时,需采用伪删除法。即寻找到左子树的最右节点即左子树的最大值,或者是右子树的最左节点即右子树的最小值。然后赋值,最后转换为在左子树或者右子树删除节点。

删除节点时需要判断,其位于父节点的左子树还是右子树。

6.1 非递归实现

		bool Erase(const K& key){Node* parent = nullptr;Node* cur = _root;while (cur){if (key < cur->_key){parent = cur;cur = cur->_left;}else if (key > cur->_key){parent = cur;cur = cur->_right;}else{//1.左为空if (cur->_left == nullptr){//判断是否删除的是根节点if (cur == _root){_root = cur->_right;}else{//判断cur为parent左子树还是右子树if (cur == parent->_left)parent->_left = cur->_right;elseparent->_right = cur->_right;}delete cur;cur = nullptr;}//2.右为空else if (cur->_right == nullptr){//判断是不是为根节点if (cur == _root){_root = cur->_left;}//判断cur为parent左子树还是右子树else{if (cur == parent->_left)parent->_left = cur->_left;elseparent->_right = cur->_left;}delete cur;cur = nullptr;}else//左右都不为空{//选择右子树的最左节点替换Node* pminRight = cur;Node* minRight = cur->_right;while (minRight->_left){pminRight = minRight;minRight = minRight->_left;}cur->_key = minRight->_key;//判断删除节点位于父节点哪一侧if (pminRight->_left == minRight){pminRight->_left = minRight->_right;}else{pminRight->_right = minRight->_right;}delete minRight;minRight = nullptr;}return true;}}return false;}

6.2 递归实现

递归实现采用引用传参,这时 root 就是 parent 左子树或者右子树的地址。当删除节点的左子树为空时,我们直接将右子树赋值给 root,如果右子树为空,我们直接将左子树赋给 root。当左右子树都不为空时,我们可以采用伪删除法将问题转换为在左子树或右子树删除某个叶子节点,从而实现递归调用。当然我们需要提前保存要删除节点,防止更新之后找不到。

bool EraseR(const K& key)
{return _EraseR(_root, key);
}
bool _EraseR(Node*& root, const K& key)
{if (root == nullptr){return false;}if (key < root->_key){return _EraseR(root->_left, key);}else if (key > root->_key){return _EraseR(root->_right, key);}//找到删除节点else{//提前保存Node* del = root;//引用传参直接修改if (root->_left == nullptr){root = root->_right;}else if (root->_right == nullptr){root = root->_left;}else{//找到左边的最右节点Node* maxLeft = root->_left;while (maxLeft->_right){maxLeft = maxLeft->_right;}swap(maxLeft->_key, root->_key);//递归在左子树删除return _EraseR(root->_left, key);}delete del;return true;}
}

7. 判断是否为二叉搜索树

为了验证我们最后生成的二叉树是二叉搜索树,我们无法通过肉眼观察,所以最好直接写一个函数判断。

通过二叉搜索树的定义,我们可以设计一个递归函数 _isValidBST(root, lower, upper) 来递归判断,函数表示考虑以 root 为根的子树,判断子树中所有节点的值是否都在 (lower,upper) 的开区间范围内。如果 root 节点的值 _key 不在(lower,upper) 的范围内说明不满足条件直接返回,否则我们要继续递归调用检查它的左右子树是否满足,如果都满足才说明这是一棵二叉搜索树。

		bool isValidBST(Node* root){return _isValidBST(root, LONG_MIN, LONG_MAX);}bool _isValidBST(Node* root, long long lower, long long upper){if (root == nullptr){return true;}if (root->_key <= lower || root->_key >= upper){return false;}bool left = _isValidBST(root->_left, lower, root->_key);bool right = _isValidBST(root->_right, root->_key, upper);return left && right;}

8. 二叉搜索树的应用

8.1 K 模型

K 模型即只有 _key 作为关键码,结构中只存储 Key,关键码即为需要搜索的值。比如需要判断某个单词是否拼写正确,首先我们将词库中所有单词集合里的每个单词作为关键码(key)来构建一棵二叉搜索树。然后,在这棵二叉搜索树中检索给定的单词是否存在。如果存在,就表明该单词拼写正确;如果不存在,就表明拼写错误。

8.2 KV 模型

KV 模型即每一个关键码 _key,都有与之对应的值 _Value,即结构中存储 <Key, Value> 的键值对。比如需要通过电话号码找到对应的联系人,这时就可以存储 <tele_num,name> 的键值对。然后将所有集合构建一颗二叉搜索树,之后就能在这课二叉搜索树中通过电话号码查找对应的联系人。

而实现KV模型也十分简单,只需要将节点 BSTreeNode 增加一个参数 val,引入第二次模版参数V,然后根据实际情况修改一下即可。

9. 源码

9.1 K 模型

namespace K
{template<class K>struct BSTreeNode{//构造函数BSTreeNode(const K& key):_left(nullptr), _right(nullptr), _key(key){}BSTreeNode<K>* _left;//左子树BSTreeNode<K>* _right;// 右子树K _key;// 键值};template<class K>class BSTree{typedef BSTreeNode<K> Node;public://成员函数BSTree(){}BSTree(const BSTree& t){_root = copy(t._root);}BSTree<K>& operator=(const BSTree<K> t){//赋值重载swap(_root, t._root);return *this;}bool Insert(const K& key){if (_root == nullptr){_root = new Node(key);return true;}Node* parent = nullptr;//保存父节点方便插入Node* cur = _root;while (cur){if (key < cur->_key){parent = cur;cur = cur->_left;}else if (key > cur->_key){parent = cur;cur = cur->_right;}else{//相同值返回falsereturn false;}}//插入cur = new Node(key);if (key < parent->_key){//左子树parent->_left = cur;}else{parent->_right = cur;}return true;}bool 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 true;}}//找到空return false;}bool Erase(const K& key){Node* parent = nullptr;Node* cur = _root;while (cur){if (key < cur->_key){parent = cur;cur = cur->_left;}else if (key > cur->_key){parent = cur;cur = cur->_right;}else{//1.左为空if (cur->_left == nullptr){//判断是否删除的是根节点if (cur == _root){_root = cur->_right;}else{//判断cur为parent左子树还是右子树if (cur == parent->_left)parent->_left = cur->_right;elseparent->_right = cur->_right;}delete cur;cur = nullptr;}//2.右为空else if (cur->_right == nullptr){//判断是不是为根节点if (cur == _root){_root = cur->_left;}//判断cur为parent左子树还是右子树else{if (cur == parent->_left)parent->_left = cur->_left;elseparent->_right = cur->_left;}delete cur;cur = nullptr;}else//左右都不为空{//选则右子树的最左节点替换Node* pminRight = cur;Node* minRight = cur->_right;while (minRight->_left){pminRight = minRight;minRight = minRight->_left;}cur->_key = minRight->_key;//判断最左节点是右子树的根节点还是右子树的最左节点if (pminRight->_left == minRight){pminRight->_left = minRight->_right;}else{pminRight->_right = minRight->_right;}delete minRight;minRight = nullptr;}return true;}}//找不到直接返回falsereturn false;}bool isValidBST(Node* root){return _isValidBST(root, LONG_MIN, LONG_MAX);}bool FindR(const K& key){return _FindR(_root, key);}bool InsertR(const K& key){return _InsertR(_root, key);}bool EraseR(const K& key){return _EraseR(_root, key);}~BSTree(){Destroy(_root);}private:bool _isValidBST(Node* root, long long lower, long long upper){if (root == nullptr){return true;}if (root->_key <= lower || root->_key >= upper){return false;}bool left = _isValidBST(root->_left, lower, root->_key);bool right = _isValidBST(root->_right, root->_key, upper);return left && right;}bool _EraseR(Node*& root, const K& key){if (root == nullptr){return false;}if (key < root->_key){return _EraseR(root->_left, key);}else if (key > root->_key){return _EraseR(root->_right, key);}//找到删除节点else{//提前保存Node* del = root;//引用传参直接修改if (root->_left == nullptr){root = root->_right;}else if (root->_right == nullptr){root = root->_left;}else{//找到左边的最右节点Node* maxLeft = root->_left;while (maxLeft->_right){maxLeft = maxLeft->_right;}swap(maxLeft->_key, root->_key);//递归在左子树删除return _EraseR(root->_left, key);}delete del;return true;}}bool _InsertR(Node*& root, const K& key){//插入if (root == nullptr){root = new Node(key);return true;}if (key < root->_key){return _InsertR(root->_left, key);}else if (key > root->_key){return _InsertR(root->_right, key);}else{//相同返回falsereturn false;}}bool _FindR(Node* root, const K& key){if (root == nullptr){return false;}if (key < root->_key){return _FindR(root->_left, key);//左子树找}else if (key > root->_key){return _FindR(root->_right, key);}else{return true;//找到了}}Node* copy(Node* root){if (root == nullptr)return nullptr;Node* newnode = new Node(root->_key);newnode->_left = copy(root->_left);newnode->_right = copy(root->_right);return newnode;}void Destroy(Node*& root){if (root == nullptr){return;}Destroy(root->_left);Destroy(root->_right);delete root;root = nullptr;}//成员变量Node* _root = nullptr;};
}

9.2 KV 模型

namespace KV
{template<class K,class V>struct BSTreeNode{//构造函数BSTreeNode(const K& key,const V&val):_left(nullptr), _right(nullptr), _key(key),_val(val){}BSTreeNode<K,V>* _left;//左子树BSTreeNode<K,V>* _right;// 右子树K _key;// 键值V _val;};template<class K,class V>class BSTree{typedef BSTreeNode<K,V> Node;public://成员函数BSTree(){}BSTree(const BSTree<K,V>& t){_root = copy(t._root);}BSTree<K,V>& operator=(const BSTree<K,V> t){//赋值重载swap(_root, t._root);return *this;}bool Insert(const K& key,const V&val){if (_root == nullptr){_root = new Node(key,val);return true;}Node* parent = nullptr;//保存父节点方便插入Node* cur = _root;while (cur){if (key < cur->_key){parent = cur;cur = cur->_left;}else if (key > cur->_key){parent = cur;cur = cur->_right;}else{//相同值返回falsereturn false;}}//插入cur = new Node(key,val);if (key < parent->_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 nullptr;}bool Erase(const K& key){Node* parent = nullptr;Node* cur = _root;while (cur){if (key < cur->_key){parent = cur;cur = cur->_left;}else if (key > cur->_key){parent = cur;cur = cur->_right;}else{//1.左为空if (cur->_left == nullptr){//判断是否删除的是根节点if (cur == _root){_root = cur->_right;}else{//判断cur为parent左子树还是右子树if (cur == parent->_left)parent->_left = cur->_right;elseparent->_right = cur->_right;}delete cur;cur = nullptr;}//2.右为空else if (cur->_right == nullptr){//判断是不是为根节点if (cur == _root){_root = cur->_left;}//判断cur为parent左子树还是右子树else{if (cur == parent->_left)parent->_left = cur->_left;elseparent->_right = cur->_left;}delete cur;cur = nullptr;}else//左右都不为空{//选则右子树的最左节点替换Node* pminRight = cur;Node* minRight = cur->_right;while (minRight->_left){pminRight = minRight;minRight = minRight->_left;}cur->_key = minRight->_key;//判断最左节点是右子树的根节点还是右子树的最左节点if (pminRight->_left == minRight){pminRight->_left = minRight->_right;}else{pminRight->_right = minRight->_right;}delete minRight;minRight = nullptr;}return true;}}//找不到直接返回falsereturn false;}bool isValidBST(Node* root){return _isValidBST(root, LONG_MIN, LONG_MAX);}Node* FindR(const K& key){return _FindR(_root, key);}bool InsertR(const K& key,const V&val){return _InsertR(_root, key, val);}bool EraseR(const K& key){return _EraseR(_root, key);}~BSTree(){Destroy(_root);}private:bool _isValidBST(Node* root, long long lower, long long upper){if (root == nullptr){return true;}if (root->_key <= lower || root->_key >= upper){return false;}bool left = helper(root->_left, lower, root->_key);bool right = helper(root->_right, root->_key, upper);return left && right;}bool _EraseR(Node*& root, const K& key){if (root == nullptr){return false;}if (key < root->_key){return _EraseR(root->_left, key);}else if (key > root->_key){return _EraseR(root->_right, key);}//找到删除节点else{//提前保存Node* del = root;//引用传参直接修改if (root->_left == nullptr){root = root->_right;}else if (root->_right == nullptr){root = root->_left;}else{//找到左边的最右节点Node* maxLeft = root->_left;while (maxLeft->_right){maxLeft = maxLeft->_right;}swap(maxLeft->_key, root->_key);//递归在左子树删除return _EraseR(root->_left, key);}delete del;return true;}}bool _InsertR(Node*& root, const K& key, const V&val){//插入if (root == nullptr){root = new Node(key,val);return true;}if (key < root->_key){return _InsertR(root->_left, key, val);}else if (key > root->_key){return _InsertR(root->_right, key, val);}else{//相同返回falsereturn false;}}Node*_FindR(Node* root, const K& key){if (root == nullptr){return false;}if (key < root->_key){return _FindR(root->_left, key);//左子树找}else if (key > root->_key){return _FindR(root->_right, key);}else{return root;//找到了}}Node* copy(Node* root){if (root == nullptr)return nullptr;Node* newnode = new Node(root->_key,root->_val);newnode->_left = copy(root->_left);newnode->_right = copy(root->_right);return newnode;}void Destroy(Node*& root){if (root == nullptr){return;}Destroy(root->_left);Destroy(root->_right);delete root;root = nullptr;}//成员变量Node* _root = nullptr;};
}
;}delete del;return true;}}bool _InsertR(Node*& root, const K& key, const V&val){//插入if (root == nullptr){root = new Node(key,val);return true;}if (key < root->_key){return _InsertR(root->_left, key, val);}else if (key > root->_key){return _InsertR(root->_right, key, val);}else{//相同返回falsereturn false;}}Node*_FindR(Node* root, const K& key){if (root == nullptr){return false;}if (key < root->_key){return _FindR(root->_left, key);//左子树找}else if (key > root->_key){return _FindR(root->_right, key);}else{return root;//找到了}}Node* copy(Node* root){if (root == nullptr)return nullptr;Node* newnode = new Node(root->_key,root->_val);newnode->_left = copy(root->_left);newnode->_right = copy(root->_right);return newnode;}void Destroy(Node*& root){if (root == nullptr){return;}Destroy(root->_left);Destroy(root->_right);delete root;root = nullptr;}//成员变量Node* _root = nullptr;};
}
http://www.dtcms.com/a/446152.html

相关文章:

  • 九亭镇村镇建设办官方网站1688接代加工订单
  • GJOI 9.27/10.3 题解
  • Python实例入门
  • 多线程核心知识点与高并发应用指南
  • 南宁网站建设nnxun政策变了2022二建有必要考吗
  • ASP3605电源芯片关键指标测试说明
  • Spring——事件机制
  • UMI企业智脑4.0与5.0的先进性之争,从“AI工具”到“孪生数字人”,赋能每个员工
  • 城乡建设查询网站网站维护包括
  • 从国标到自动化:VSTO实现身份证智能解析(待测)
  • 租凭境外服务器做违规网站wordpress 幻灯片主题
  • 网站开发团队简介如何写链接网站制作
  • php 8.4.5 更新日志
  • MongoDB 连接时的**认证参数配置错误**
  • 大兴安岭做网站葫芦岛建设工程信息网站
  • 商标设计网站提供哪些服务建筑书店
  • 除 OpenAI/GPT-4o 等主流头部产品外,值得关注的 AI 及 Agent 产品有哪些?
  • Vue 3 —— M / 接口文档
  • 【办公类-109-06】20250916圆牌卡片15CM手工纸+动物头像+拼音表+word单面编辑
  • 服务器搭建网站制作网站怎么用图片做背景
  • 搭建网站空间无印良品vi设计分析
  • 做pc端网站资讯seo诊断工具有哪些
  • 高层次综合基础-vivado hls第三章
  • 网站建设单位不给数据库苏州网络公司工作室
  • windows部署网站phpwordpress教程网页修改
  • 前端GIS篇——WebGIS、WebGL、Java后端篇
  • 网站开发语言分析网站制作想法
  • 做抽奖网站合法吗网站底部备案代码
  • 基于SGLang的推理服务业务实战部署方案(直接可用)
  • 秦皇岛seo网站推广吉林省吉林市是几线城市