二叉搜索树

  • 概念
    • 二叉搜索树的概念(特征)
    • 二叉搜索树的性能分析
  • 操作
    • 查找
    • 插入
    • 删除
  • 模拟实现
    • 搜索二叉树类的构建
    • 查找操作实现
    • 插入操作实现
    • 删除操作实现
      • 递归版本
  • 二叉搜索树的应用
    • K模型
    • KV模型

本文将介绍二叉搜索树,将为后面set与map的学习作铺垫。

概念

二叉搜索树的概念(特征)

二叉搜索树又称二叉排序树,它或是一颗空树,或者是其左子树上所有孩子结点的值小于根节点的值,且右子树上所有孩子结点的值大于根节点的值同时,其根节点左右子树也分别为二叉搜索树。
比如如下所示的二叉树就是一颗二叉搜索树,int a [] = {5,3,4,1,7,8,2,6,0,9};

二叉搜索树的性能分析

在之前数据结构中我们知道分析一种结构要分析其时间复杂度以及空间复杂度。那么我们来分析一下二叉搜索树查找的效率,首先由于二叉搜索树的特征,对有n个结点的二叉搜索树,若每个元素查找的概率相等,则二叉搜索树平均查找长度是结点在二叉搜索树的深度的函数,即结点越深,则比较次数越多。
但对于同一个关键码集合,如果各关键码插入的次序不同,可能得到不同结构的二叉搜索树:

如上图所示,最优情况下,二叉搜索树为完全二叉树,其平均比较次数为:log2N;最差情况下,二叉搜索树退化为单支树,其平均比较次数为:N/2

操作

查找

由于搜索二叉树的特征,其查找无需遍历整棵树,只要通过key值的比较即可快速确定要找的结点:

插入

二叉搜索树的插入操作并不算复杂。具体过程如下:

删除

二叉树的删除较为复杂,具体要分为以下几种情况:

  1. 空树
  2. 叶子结点(没有孩子结点)
  3. 只有一个孩子
  4. 有两个孩子
    首先,对于第一种情况,即空树,返回false即可;其次,对于第二种情况,直接删除叶子结点;然后,对于第三种情况,若要删除的结点只有左孩子结点,则删除该结点且使被删除节点的双亲结点指向被删除节点的左孩子结点,若要删除的结点只有右孩子结点,则删除该结点且使被删除节点的双亲结点指向被删除结点的右孩子结点。
    最后,对于第四种情况,我们采用替代法删除结点,即找到该节点左子树中key值最大的结点或者找到其右子树key值最小的结点,把该节点的值赋给要删除的结点,然后将这个替代的结点删除即可。

模拟实现

这里我们实现key-value二叉搜索树模型。

搜索二叉树类的构建

在实现二叉搜索树之前先将其类的大致轮廓实现:

template <class K, class V>
struct BSTreeNode//二叉搜索树结点
{BSTreeNode<K, V>* _left;BSTreeNode<K, V>* _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//二叉搜索树,BinarySearchTree
{public:typedef BSTreeNode<K, V> Node;
private:Node* _root = nullptr;
};

查找操作实现

由上述介绍的逻辑不难写出二叉搜索树的查找操作代码:

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

插入操作实现

由于我们这里实现的二叉搜索树不允许数据冗余,故可能出现插入失败的情况,因此,插入操作的返回值设置为bool,具体操作如下:

 bool Insert(const K& key, const V& value){if (_root == nullptr){_root = new Node(key, value);return true;}Node* cur = _root;Node* parent = _root;while (cur){parent = cur;if (key > cur->_key)cur = cur->_right;else if (key < cur->_key)cur = cur->_left;else//数据冗余,不插入return false;}//找到要插入的位置cur = new Node(key, value);if (key > parent->_key)parent->_right = cur;elseparent->_left = cur;return true;}

注意,这里由于要保留链接关系,因此需要定义一个父亲指针始终指向cur的父亲结点。

删除操作实现

删除操作的逻辑稍显复杂,与插入操作类似的是,同样要设置父亲指针保证链接关系;其次通过画图可以很容易的将思路转化为代码:

 bool Erase(const K& key){if (_root == nullptr)return false;Node* cur = _root;Node* parent = _root;//先找到要删的结点while (cur){if (key > cur->_key){parent = cur;cur = cur->_right;}else if (key < cur->_key){parent = cur;cur = cur->_left;}elsebreak;}if (cur == nullptr)//没有找到要删的结点return false;//要删的结点有一个孩子问空if (cur->_left == nullptr){if (cur == parent->_left)parent->_left = cur->_left;else if (cur == parent->_right)parent->_right = cur->_left;else_root = parent->_right;}else if (cur->_right == nullptr){if (cur == parent->_left)parent->_left = cur->_right;else if (cur == parent->_right)parent->_right = cur->_right;else_root = parent->_left;}else//要删的结点两个孩子都不为空{//先找到该结点右子树最小的结点Node* minRight = cur->_right;Node* minRight_parent = cur;while (minRight->_left){minRight_parent = minRight;minRight = minRight->_left;}cur->_key = minRight->_key;cur->_value = minRight->_value;cur = minRight;if (minRight_parent == _root)minRight_parent->_right = cur->_right;elseminRight_parent->_left = cur->_right;}delete cur;return true;}

递归版本

这里稍微介绍以下几个操作的递归版本,由于递归的性能较差,且占用空间较多,故稍作了解即可。
在递归操作中,非常巧妙的使用了引用传值,从而保证在不定义父亲指针的情况下保留了链接关系。

public:bool InsertR(const K& key, const V& value){return _InsertR(_root, key, value);}Node* FindR(const K& key){return _FindR(_root, key);}bool EraseR(const K& key){return _EraseR(_root, key);}
private:bool _InsertR(Node*& root, const K& key, const V& value){if (root == nullptr)//找到要插入的位置{//由于传入的是引用,故链接关系保留root = new Node(key, value);return true;}if (key > root->_key)_InsertR(root->_right, key, value);else if (key < root->_key)_InsertR(root->_left, key, value);elsereturn false;}Node* _FindR(Node* root, const K& key){if (root == nullptr)return nullptr;if (key > root->_key)_FindR(root->_right, key);else if (key < root->_key)_FindR(root->_left, key);elsereturn root;}bool _EraseR(Node*& root, const K& key){if (root == nullptr)//没有找到要删除的结点return false;if (key > root->_key)_EraseR(root->_right, key);else if (key < root->_key)_EraseR(root->_left, key);else//找到要删除的结点{//两个孩子中有空Node* del = root;if (root->_left == nullptr)root = root->_right;//由于是引用,故链接关系不变else if (root->_right == nullptr)root = root->_left;else//两个孩子均非空{Node* minRight = root->_right;Node* minRight_parent = root;while (minRight->_left)//找到右子树中最小的结点minRight = minRight->_left;root->_key = minRight->_key;root->_value = minRight->_value;//通过递归删除替代结点return _EraseR(root->_right, minRight->_key);//while (minRight->_left)//找到右子树中最小的结点//{// minRight_parent = minRight;//  minRight = minRight->_left;//}//root->_key = minRight->_key;//root->_value = minRight->_value;//if (minRight_parent == root)//  minRight_parent->_right = minRight->_right;//else//  minRight_parent->_left = minRight_parent->_right;//del = minRight;}delete del;return true;}}

二叉搜索树的应用

K模型

K模型:K模型即只有key作为关键码,结构中只需要存储Key即可,关键码即为需要搜索到的值。
比如:给一个单词word,判断该单词是否拼写正确,具体方式如下:
以单词集合中的每个单词作为key,构建一棵二叉搜索树。
在二叉搜索树中检索该单词是否存在,存在则拼写正确,不存在则拼写错误。

KV模型

KV模型:每一个关键码key,都有与之对应的值Value,即<Key, Value>的键值对。该种方式在现实生活中非常常见:比如英汉词典就是英文与中文的对应关系,通过英文可以快速找到与其对应的中文,英文单词与其对应的中文<word, chinese>就构成一种键值对;再比如统计单词次数,统计成功后,给定单词就可快速找到其出现的次数,单词与其出现次数就是<word, count>就构成一种键值对。
比如:实现一个简单的英汉词典dict,可以通过英文找到与其对应的中文,具体实现方式如下:
<单词,中文含义>为键值对构造二叉搜索树,注意:二叉搜索树需要比较,键值对比较时只比较Key查询英文单词时,只需给出英文单词,就可快速找到与其对应的key。

//统计词语出现的次数
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();

【C++】二叉搜索树解析相关推荐

  1. 《数据结构》网课 邓俊辉 习题详细解析(第七章:二叉搜索树)

    文章目录 (a)概述 (b1)BST:查找 (b2)BST:插入 (b3)BTS:删除 (c)平衡与等价 (d1)AVL树:重平衡 (d2)AVL树:插入 (d3)AVL树:删除 (d4)AVL树:( ...

  2. bst java_BST(二叉搜索树) Java 实现解析

    1.二叉搜索树的定义:一颗树的所有左子树都比根小,所有右子树都比根大,成为二叉搜索树. 2.该BST树实现了9个重要方法,分别是关键字查找,插入,删除,删除节点后续节点查找,前序遍历,中序遍历,后序遍 ...

  3. 【数据结构与算法】之深入解析“修剪二叉搜索树”的求解思路与算法示例

    一.题目要求 给你二叉搜索树的根节点 root,同时给定最小边界 low 和最大边界 high,通过修剪二叉搜索树,使得所有节点的值在 [low, high] 中. 修剪树不应该改变保留在树中的元素的 ...

  4. 【数据结构与算法】之深入解析“把二叉搜索树转换为累加树”和“从二叉搜索树到更大和树”的求解思路与算法示例

    一.题目要求 ① 把二叉搜索树转换为累加树 给出二叉搜索树的根节点,该树的节点值各不相同,请将其转换为累加树(Greater Sum Tree),使每个节点 node 的新值等于原树中大于或等于 no ...

  5. 【数据结构与算法】之深入解析“将有序数组转换为二叉搜索树”的求解思路与算法示例

    一.题目要求 给你一个整数数组 nums ,其中元素已经按升序排列,请你将其转换为一棵高度平衡 二叉搜索树. 高度平衡二叉树是一棵满足「每个节点的左右两个子树的高度差的绝对值不超过 1 」的二叉树. ...

  6. 【数据结构与算法】之深入解析“恢复二叉搜索树”的求解思路与算法示例

    一.题目要求 给你二叉搜索树的根节点 root ,该树中的恰好两个节点的值被错误地交换.请在不改变其结构的情况下,恢复这棵树. 示例 1: 输入:root = [1,3,null,null,2] 输出 ...

  7. 【数据结构与算法】之深入解析“验证二叉搜索树”的求解思路与算法示例

    一.题目要求 给你一个二叉树的根节点 root ,判断其是否是一个有效的二叉搜索树. 有效二叉搜索树定义如下: 节点的左子树只包含小于当前节点的数. 节点的右子树只包含大于当前节点的数. 所有左子树和 ...

  8. 【数据结构与算法】之深入解析“不同的二叉搜索树II”的求解思路与算法示例

    一.题目要求 给你一个整数 n ,请你生成并返回所有由 n 个节点组成且节点值从 1 到 n 互不相同的不同二叉搜索树,可以按 任意顺序 返回答案. 示例 1: 输入:n = 3 输出:[[1,nul ...

  9. 【数据结构与算法】之深入解析“不同的二叉搜索树”的求解思路与算法示例

    一.题目要求 给你一个整数 n ,求恰由 n 个节点组成且节点值从 1 到 n 互不相同的二叉搜索树有多少种?返回满足题意的二叉搜索树的种数. 示例 1: 输入:n = 3 输出:5 示例 2: 输入 ...

最新文章

  1. 又一个巨头决定彻底放弃微服务,全面拥抱K8S!
  2. Springboot中使用jpa
  3. P1047 [NOIP2005 普及组] 校门外的树(python3实现)
  4. Android 系统(275)---Mtk Webview基本问题
  5. 两坐标点的直线距离c语言,c语言求平面上2个坐标点的直线距离、求俩坐标直线距离作为半径的圆的面积、递归、菲波那次数列、explode...
  6. Adobe Acrobat Reader DC缺少字体包的问题
  7. vb adodc连接mysql_VB之Adodc控件连接数据库
  8. RS485芯片UN485E的特点及其应用
  9. 社会学概论(本专)【1】
  10. 香港中文大学计算机系直博面试经验
  11. 个人项目-仿微信小打卡小程序
  12. 海报与Banner的区别
  13. Opencv 基础(三):使用OpenCV裁剪图像
  14. python模拟鼠标点击和键盘输入的操作_python模拟鼠标点击和键盘输入的操作
  15. java生成短信验证码_Java随机生成手机短信验证码的方法
  16. 上半年精选300篇Python文章,推荐你收藏~
  17. STM32F40x 最小系统
  18. jni-Demo-基于linux(c++ java)
  19. matlab 牛顿法 初值,非线性方程的数值解法:牛顿法及牛顿下山法(含Matlab程序)...
  20. 2019年3月计算机二级考试题库百度云,2019年3月计算机等级考试题库-二级MS Office试题...

热门文章

  1. python定义一个复数类complex、内有两个成员变量_python应用(5):变量类型与数据结构...
  2. JavaScript 多维数组
  3. 双馈风力发电系统仿真,DFIG,采用背靠背双pwm变换器,具备最大功率追踪功能
  4. 我的世界服务器显示防火墙检测,win7|win2008防火墙为mc服务器开放端口的方法
  5. 快速导入机智云APP并且修改ui界面
  6. php ip 城市,php如何根据IP地址获取所在城市
  7. 提高视力的有效方法是什么?
  8. 4个超级实用的黑科技软件神器,让你爱不释手!
  9. 在kd下查看CR3寄存器
  10. 【微信小程序控制硬件15 】 开源一个微信小程序,支持蓝牙快速配网+WiFi双控制ESP32-C3应用示范;(附带Demo)