【C++】搜索二叉树

1. 二叉搜索树

a. 二叉搜索树的概念

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

  1. 若它的左子树不为空,则左子树上所有节点的值都小于根节点的值
  2. 若它的右子树不为空,则右子树上所有节点的值都大于根节点的值
  3. 它的左右子树也分别为二叉搜索树
  4. 它的中序遍历是得到的结果是升序

b. 二叉搜索树的实现

1. 搜索二叉树的构建

代码

template<class K>
struct BinNode
{BinNode(K key):_key(key){}K  _key;BinNode* left = nullptr;BinNode* right = nullptr;
};
template<class K>
class BinNodeTree
{
public:typedef BinNode<K> node;
private:node* _root = nullptr;
};

2. 二叉树的插入 

循环实现思路

返回类型是 bool ,判断能否插入传入的值(二叉搜索树不存相同的值)

跟节点比较,如果 插入的值 key > 节点的值,则走节点的右子树 ; 如果插入的值 key < 节点的值,则走左子树 ; 如果等于,返回 false ;如果结点为空 ,结束循环 ,new 一个新的结点,需要空节点的父节点去链接新结点,所以我们一定要定义一个父节点

注意:

  1. 由于不知道新结点应该是父节点的左子树还是右子树,这里链接就需要判断一下,如果空结点是父结点的左子树,那么就链接左边,反之亦然
  2. 一开始的根节点为空,所以这里需要特殊处理一下,让根节点成为插入的第一个值构造的新节点

代码

bool insert(const K &key)
{if (_root == nullptr){_root = new node(key);return true;}node* prev = _root;node* cur = _root;while (cur){if (key > cur->_key){prev = cur;cur = cur->right;}else if (key < cur->_key){prev = cur;cur = cur->left;}else{return false;}}cur = new node(key);if (key > prev->_key){prev->right = cur;}else{prev->left = cur;}return true;
}

 递归实现思路

大体思路和循环的思路差不多,但是略有不同

  1. 由于递归函数一定需要结点的地址(可以走左右子树)和 需要插入的值,给这个递归函数传入的结点的地址一定是根节点,但是根节点在类里面不能访问,所以这里我们就弄一层包装,一个函数是传根节点的地址的,另一个函数用来递归实现(都在类里面)
  2. 这里我们同样需要判断插入的值 key 和 结点的值的关系,但是这里可以不需要父节点,我们在递归函数的形参上面跟之前有所不同,这里的形参用了引用,使得可以直接让空结点存新结点的地址

代码

bool Insert(const K& key)
{return _Insert(_root, key);
}
bool _Insert(node*& root, const K& key)
{if (root == nullptr){root = new node(key);return true;}if (key == root->_key){return false;}else if (key > root->_key){_Insert(root->right, key);}else{_Insert(root->left, key);}
}

   3. 二叉树的查找

循环实现思路

跟节点比较,如果 插入的值 key > 节点的值,则走节点的右子树 ; 如果插入的值 key < 节点的值,则走左子树 ; 如果结点为空 return false;

代码

bool find(const K& key)
{node* cur = _root;if (cur == nullptr){return false;}while (cur){if (key > cur->_key){cur = cur->right;}else if(key < cur->_key){cur = cur->left;}else{return true;}}return false;
}

 

递归实现思路

和插入的递归思路有一点相同,都要封装一层

剩下的思路和循环是一样的

代码

bool Find(const K& key)
{return _Find(_root, key);
}
bool _Find(node* root, const K& key)
{if (root == nullptr){return false;}if (key == root->_key){return true;}else if (key > root->_key){_Find(root->right, key);}else{_Find(root->left, key);}
}

4. 二叉树的删除

循环实现思路

大体上遇到的情况分三种情况:

  1. 删除的结点左右子树为空
  2. 删除的结点左子树或者右子树为空
  3. 删除的结点左右子树都不为空

第一种情况:

实际上,第一种情况的操作可以归到第二种里面

第二种情况:

如果删除的结点左子树为空,那么我们需要这个结点的 父节点的左子树或者右子树(根据删除结点是父节点的左子树还是右子树进行判断) 是删除结点的右子树

如果删除结点是父节点的左子树,那么父节点左边链接,反之则相反

如图:

如果删除的结点右子树为空,那么我们需要这个结点的 父节点的 左子树或者右子树 (根据删除结点是父节点的左子树还是右子树进行判断)是删除结点的左子树

如果删除结点是父节点的左子树,那么父节点左边链接,反之则相反

如图:

前者我们说了,如果删除节点两边都为空,则也归到第二种,此时只要判断删除节点是父节点的左子树还是右子树就好了,无需管链接删除节点的左子树还是右子树

如图:


 

第三种情况:

由于两边都不为空,我们不好直接删除,这时候,我们需要把删除节点当根节点(同样构成搜索二叉树),找这个搜索二叉树的某个节点的值,既可以比左边节点的值大(除根节点外),也可以比右边节点的值小,有两个答案:这个搜索二叉树的左子树的最大值和右子树的最小值

如图:

如何找右子树的最小值呢?先得到右子树的地址,再一直往左走,直到节点为空,则它的父节点就是我们要找的,所以我们需要定义一个父节点,让删除节点存父节点的值,由于父节点的左子树为空,那么删除节点要链接父节点的右子树,跟之前第二种情况一样,要知道父节点是上一个父节点的左子树还是右子树(避免删除节点就是根节点的情况导致的错误),再删除父节点

找左子树的最大值相同道理

注意事项:

  1. 第二种情况有特例,可能删除的是根节点,并且左子树或者右子树为空

如果左子树为空,这个时候我们只需要直接让根节点存右子树的地址,释放原来的节点

反之则相反(如果同样为左右子树都为空,同样适用)

如图:

  1. 第三种情况一定要注意本来定义的两个指针(一前一后),最开始初始化时,都指向删除节点的地址,不要其中一个置空(防止删除的节点是父节点,而导致置空节点不能进入循环发生的一系列错误)

代码

bool erase(const K &key)
{node* parent = _root;node* cur = _root;while (cur){if (key > cur->_key){parent = cur;cur = cur->right;}else if (key < cur->_key){parent = cur;cur = cur->left;}else{if (cur->left == nullptr){if (cur == _root){_root = cur->right;}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;}if (parent->left == cur){parent->left = cur->left;}else{parent->right = cur->left;}delete cur;}else{node* MinRight = cur->right;node* pMinRight = cur;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;}return true;}}return false;
}

 递归实现思路

和插入的递归思路有些一样,都需要包装,都需要传指针的引用

第一种情况和第二种情况和循环思路很像,由于是引用,这里我们不需要判断是左子树还是右子树,直接赋值即可

第三种情况前面还是一样,找到左子树的最大值或者右子树的最小值

如果找左子树的最大值:

最大值我们可以在通过循环来找,找到之后,可以选择交换删除节点的值和最大值,再次进行递归,删除的值key不变

或者是只让删除节点的值换成最大值,其它不变,递归时,传的根节点就是删除节点的左子树,删除的值key就是最大值

注意:

第三种情况递归时,不可以直接传存最大值的节点(那个最大值节点是局部变量,而引用接收局部变量会出很大问题)

代码

bool Erase(const K& key)
{return _Erase(_root,key);
}
bool _Erase(node*& root,const K& key)
{if (root == nullptr){return false;}if (key > root->_key){_Erase(root->right, key);}else if(key < root->_key){_Erase(root->left, key);}else{node* cur = root;if (root->left == nullptr){root = root->right;delete cur;}else if (root->right == nullptr){root = root->left;delete cur;}else{cur = cur->left;while (cur->right){cur = cur->right;}int k = root->_key = cur->_key;_Erase(root->left, k);}return true;}
}

5.  二叉树的销毁

代码

~BinNodeTree()
{_BinNodeTree(_root);
}
void _BinNodeTree(node* root)
{if (root == nullptr){return;}_BinNodeTree(root->left);_BinNodeTree(root->right);delete root;
}

后序遍历即可 

6.  二叉树的拷贝构造

代码

BinNodeTree(const BinNodeTree<K>& t)
{_root = copy(t._root,_root);
}
node* copy(node* t1, node* t2)
{if (t1 == nullptr){return nullptr;}t2 = new node(t1->_key);t2->left = copy(t1->left, t2->left);t2->right = copy(t1->right, t2->right);return t2;
}

2. 二叉搜索树的应用

  1. K模型:K模型即只有key作为关键码,结构中只需要存储Key即可,关键码即为需要搜索到

的值

如:查找一个单词是否拼写正确

    2. KV模型:每一个关键码key,都有与之对应的值Value,即<Key, Value>的键值对,该种方

式在现实生活中非常常见

如:单词中英文查找 , 统计单词出现次数

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

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

相关文章

专题二_滑动窗口(1)

目录 209. 长度最小的子数组 解析 题解 3. 无重复字符的最长子串 解析 题解 1004. 最大连续1的个数 III 解析 题解 209. 长度最小的子数组 209. 长度最小的子数组 - 力扣&#xff08;LeetCode&#xff09; 解析 题解 class Solution { public:int minSubArrayLen(int…

DREAM: A Dynamic Scheduler for Dynamic Real-time Multi-model ML Workloads——论文泛读

ASPLOS 2024 Paper 论文阅读笔记整理 问题 新兴的实时多模型ML&#xff08;RTMM&#xff09;工作负载&#xff0c;如AR/VR和无人机控制&#xff0c;涉及各种粒度的动态行为&#xff1a;任务、模型和模型中的层。这种动态行为给ML系统中的系统软件带来了新的挑战&#xff0c;与…

大话设计模式之装饰模式

装饰模式&#xff08;Decorator Pattern&#xff09;是一种结构型设计模式&#xff0c;它允许向现有对象动态地添加新功能&#xff0c;同时又不改变其结构。装饰模式通过将对象放入包装器中来实现&#xff0c;在包装器中可以动态地添加功能。 在装饰模式中&#xff0c;通常会有…

吉时利KEITHLEY DMM7510数字万用表

181/2461/8938产品概述&#xff1a; Keithley DMM7510 结合了精密数字万用表、图形触摸屏显示器和高速、高分辨率数字转换器的所有优点&#xff0c;创造了业界第一&#xff1a;图形采样万用表。利用 DMM7510 的电压或电流数字化功能&#xff0c;捕获和显示波形和瞬态事件变得更…

【详细讲解PostCSS如何安装和使用】

&#x1f308;个人主页:程序员不想敲代码啊&#x1f308; &#x1f3c6;CSDN优质创作者&#xff0c;CSDN实力新星&#xff0c;CSDN博客专家&#x1f3c6; &#x1f44d;点赞⭐评论⭐收藏 &#x1f91d; 希望本文对您有所裨益&#xff0c;如有不足之处&#xff0c;欢迎在评论区提…

基于资源的约束委派(中)

烂番茄 基于资源的约束委派通过修改自身msDS-AllowedToActOnBehalfOfOtherIdentity字段达到委派的目的&#xff0c;默认把这台域机器拉入域的域 用户有这个权限&#xff0c;还有谁有&#xff1f;因为evil这台机器通过 07 用户拉入域内&#xff0c;通过AdFind遍历evil的ACL&…

SQL107 将两个 SELECT 语句结合起来(二)(不用union,在where里用or)

select prod_id,quantity from OrderItems where quantity 100 or prod_id like BNBG% order by prod_id;在where子句里使用or

【设计模式】中介者模式的应用

文章目录 1.概述2.中介者模式的适用场景2.1.用户界面事件2.2.分布式架构多模块通信 3.总结 1.概述 中介者模式&#xff08;Mediator Pattern&#xff09;是一种行为型设计模式&#xff0c;它用于解决对象间复杂、过度耦合的问题。当多个对象&#xff08;一般是两个以上的对象&…

自定义你的商店 – 设计WooCommerce商店的新方法

WooCommerce 8.8即将推出&#xff0c;带来了一种无需代码即可创建精美商店的新方法。向“自定义你的商店”问好&#xff0c;这是一项全新功能&#xff0c;将取代“个性化你的商店”入门步骤。 自定义你的商店将利用最新的WordPress站点编辑工具以及酷炫的新Pattern Assembler …

深入浅出:探索Hadoop生态系统的核心组件与技术架构

目录 前言 HDFS Yarn Hive HBase Spark及Spark Streaming 书本与课程推荐 关于作者&#xff1a; 推荐理由&#xff1a; 作者直播推荐&#xff1a; 前言 进入大数据阶段就意味着 进入NoSQL阶段&#xff0c;更多的是面向OLAP场景&#xff0c;即数据仓库、BI应用等。 …

【系统架构师】-第13章-层次式架构设计

层次式体系结构设计是将系统组成一个层次结构&#xff0c;每一层 为上层服务 &#xff0c;并作为下层客户。 在一些层次系统中&#xff0c;除了一些精心挑选的输出函数外&#xff0c; 内部的层接口只对相邻的层可见 。 连接件通过决定层间如何交互的协议来定义&#xff0c;拓扑…

HelpLook AI ChatBot:自定义Prompts综合指南

AI问答机器人&#xff08;AI Chatbot&#xff09;日益在各行业普及&#xff0c;但回答准确率的不足仍是其面临的痛点。用户在与AI问答机器人的互动中常发现&#xff0c;机器人难以完全理解和准确回答复杂问题。HelpLook可以通过自定义提示词&#xff08;Prompts&#xff09;和集…