概念

二叉查找树(Binary Search Tree),(又:二叉搜索树,二叉排序树)它或者是一棵空树,或者是具有下列性质的二叉树:
1、若它的左子树不空,则左子树上所有结点的值均小于它的根结点的值;
2、若它的右子树不空,则右子树上所有结点的值均大于它的根结点的值;
3、它的左、右子树也分别为二叉排序树。

作用:二叉搜索树作为一种经典的数据结构,它既有链表的快速插入与删除操作的特点,又有数组快速查找的优势;所以应用十分广泛,例如在文件系统和数据库系统一般会采用这种数据结构进行高效率的排序与检索操作

二叉搜索树的实现

树的结点与树

二叉树搜索和二叉树一样,底层都是通过链表来实现的,其中节点类也是一样用有3个属性,分别是数据、左孩子、右孩子

template <class T>
struct BNode
{typedef BNode<T> Node;T _data; //数据Node* _left;//左孩子Node* _right;//右孩子BNode(const T& data):_data(data),_left(nullptr),_right(nullptr){}
};template <class T>
class BTree
{public:typedef BNode<T> Node;BTree():_root(nullptr){}
private:Node* _root;//根
};

查找

  1. 若根节点不为空:
  2. 若根节点data==查找的key,则返回所找到的节点
  3. 若根节点data > 查找的key,则在其左子树查找
  4. 若根节点data < 查找的key,则在其右子树查找
 Node* find(const T& val){Node* cur = _root;while (cur){if (cur->_data == val)return cur;else if (cur->_data > val)cur = cur->_left;elsecur = cur->_right;}//平均性能logN}

插入

搜索树默认是不会存在重复元素的

  1. 如果根为空,创建新结点并将结点置为根节点
  2. 定义一个当前遍历的结点cur和保留上一个节点,也就是cur的父亲parent
  3. 遍历树,当存在重复元素,则表示插入失败直接返回false;若当前节点值比插入的值大,则要将改值插入到左子树中;若当前节点值比插入的值小,则要将改值插入到右子树中;
  4. 插入的结点必须是叶子结点,所以cur肯定会遍历到空,而此时的parent结点,就是新结点的父节点,这也就是定义该结点的原因;如果parent结点的值大于要插入的值,则该新结点为parent的左孩子,反之为右孩子
 //不插入重复的值bool insert(const T& val){if (_root == nullptr){_root = new Node(val);return true;}Node* cur = _root;Node* parent = nullptr;while (cur){parent = cur;if (cur->_data == val)return false;else if (cur->_data > val)cur = cur->_left;elsecur = cur->_right;}cur = new Node(val);if (parent->_data > val)parent->_left = cur;elseparent->_right = cur;return true;}

测试:

转换过来,树的样子

遍历

我们这里主要讲的是中序遍历,因为只有中序遍历,才满足树的升序输出

 //由于根节点是私有的,外部不能访问,所以要进行包装void inorder(){_inorder(_root);}void _inorder(Node* root){if (root){_inorder(_root->_left);//先遍历左子树cout << root->_data << " ";//打印根节点_inorder(_root->_right);//再遍历右子树}}

测试:

拷贝

树存在指针资源,拷贝需要深拷贝。拷贝核心:由上到下创建节点并递归保存,自底向上连接节点

 //拷贝二叉搜索树的数据和结构Node* copy(Node* root){if (root == nullptr)return nullptr;Node* newNode = new Node(root->_data);newNode->_left = copy(root->_left);newNode->_right = copy(root->_right);return newNode;}BTree(const BTree<T>& btree):_root(copy(btree._root)){}

销毁与析构

  1. 先销毁左子树
  2. 再销毁右子树
  3. 最后删除根节点
 void destroy(Node* root){if (root){destroy(root->_left);destroy(root->_right);delete root;}}~BTree(){if (_root){destroy(_root);_root = nullptr;}}

删除

删除一个节点分很多种情况,有如下4种

  1. 删除的结点无左右孩子
  2. 删除的结点只有左孩子
  3. 删除的结点只有右孩子
  4. 删除的结点有左、右孩子

删除叶子结点

我们先分析第一种情况,也就是删除的是一个叶子结点。例如我们要删除4这个结点

我们先要进行从根向下搜索,找到4这个值的位置才行,如果要查找的数值在树中不存在,直接返回即可。如果找到了该结点,就要判断该结点的孩子情况,我们这里先考虑是叶子结点。如果是叶子结点,不需要额外的操作,所以在直接将该结点的位置置空后删除该结点。在代码逻辑中,要有两个指针,一个是指向当前要遍历删除的结点cur,一个是cur的父节点parent,parent结点主要是用来判断是删除当前树的左孩子还是右孩子,然后在删除结点后将其置空

代码逻辑如下

  1. 判断当前节点是否为根节点,如果是根节点,就将根节点置为空
  2. 如果不是根节点,那就将判断该结点是父节点的左边还是右边,如果是左边就将父节点的左孩子置为空,如果是右边就将父节点的右孩子置为空
  3. 删除结点
 void erase(const T& data){Node* cur = _root;Node* parent = nullptr;while (cur){   if (cur->_data == data)break;else if (cur->_data > data){parent = cur;cur = cur->_left;}            else{parent = cur;cur = cur->_right;}}//判断是否找到了需要删除的结点if (cur == nullptr)return false;//未找到数据//删除结点//1、删除的结点为叶子结点if (cur->_left == nullptr && cur->_right == nullptr){//特殊情况是否根节点if (cur == _root){_root = nullptr;}else{//需要判断当前节点的位置if (parent->_left == cur)parent->_left = nullptr;elseparent->_right = nullptr;}//删除结点delete cur;}return true;}

删除只有左孩子或者右孩子的结点

例如我们删除3这个结点

其实删掉结点3,和删除叶子结点算法类似,删除3号结点,就让3的父节点连接删除的结点的非空孩子即可。其实我们可以将删除叶子结点的那一步操作省去,可以归并到这里来,因为算法核心都是差不多的。因为叶子结点的左右孩子都为空,无论父节点连接左孩子还是右孩子,最终连接的都是空节点nullptr

此时还是一棵二叉搜索树。无论是只有左孩子还是只有右孩子,原理都差不多,只是在连接时连接的结点不同。左孩子为空,就连接删除结点的右孩子;如果右孩子为空,就连接删除结点的左孩子

     //2.存在一个孩子else if (cur->_left == nullptr)//左孩子为空,右孩子非空{//特殊情况是否根节点if (cur == _root){_root = cur->_right;}else{if (parent->_left == cur)//删除的是父节点的左边parent->_left = cur->_right);//就让父节点连接删除的结点的右孩子(左孩子为空,右孩子非空)elseparent->_right = cur->_right;//同理,无论删除的是那边,都连接的是右孩子}//删除结点delete cur;}else if (cur->_right == nullptr){//特殊情况是否根节点if (cur == _root){_root = cur->_right;}else{if (parent->_left == cur)//删除的是父节点的左边parent->_left = cur->_left);//就让父节点连接删除的结点的左孩子(左孩子非空,右孩子为空)elseparent->_right = cur->_left;//同理,无论删除的是那边,都连接的是左孩子}//删除结点delete cur;}

删除有左右孩子的结点

假如我们删除3这个结点

这时候删除的是既有左孩子也有右孩子的结点,我们可以将4连接1结点,然后再让3的父结点5连接4即可。但是如果我们复杂点,删除的是5结点,按照删除3结点的思想连接的话,8作为根节点明显是不符合的。我们是上帝视角,很容易知道如何连接,但是计算机时看不到的,计算机计算的都是能看见的结点。所以我们必须找出一个规律写成代码让计算机执行。我们这里可以借助搜索树的性质,根节点左边的结点都比根节点小,根右边的结点都比根节点大。所以我们有两种方案可选,第一是选择左子树的最右结点,此时这结点肯定左子树中最大的结点,作为根是比较合适的。第二种是选择右子树的最左结点,此时这结点肯定是右子树中最小的结点,也适合作为根

算法流程(假设选择第一种情况):
1、找到左子树的最右节点leftRightMost
2、交换需要被删除的结点的swap(cur->data,leftRightMost->data)
3、判断删除的结点是父节点的左孩子还是右孩子,将最右节点的左子树连接到父节点的左孩子或者右孩子。删除leftRightMost结点

     else{//第一种情况-找左子树的最右节点Node* leftRightMost = cur->_left;parent = cur;//此时下面用到的parent都为leftRightMost的父节点while (leftRightMost->_right)//查找最右节点{parent = leftRightMost;leftRightMost = leftRightMost->_right;}//交换swap(cur->_data, leftRightMost->_data);//删除最右节点if (parent->_left == leftRightMost)parent->_left = leftRightMost->_left;//最右节点肯定没有右孩子elseparent->_right = leftRightMost->_left;delete leftRightMost;}

erase函数总代码

 bool erase(const T& data){Node* cur = _root;Node* parent = nullptr;while (cur){if (cur->_data == data)break;else if (cur->_data > data){parent = cur;cur = cur->_left;}           else{parent = cur;cur = cur->_right;}}//判断是否找到了需要删除的结点if (cur == nullptr)return false;//未找到数据//删除结点//1、删除的结点为叶子结点if (cur->_left == nullptr && cur->_right == nullptr){//特殊情况是否根节点if (cur == _root){_root = nullptr;}else{//需要判断当前节点的位置if (parent->_left == cur)parent->_left = nullptr;elseparent->_right = nullptr;}//删除结点delete cur;}//2.存在一个孩子else if (cur->_left == nullptr)//左孩子为空,右孩子非空{//特殊情况是否根节点if (cur == _root){_root = nullptr;}else{if (parent->_left == cur)//删除的是父节点的左边parent->_left = cur->_right;//就让父节点连接删除的结点的右孩子(左孩子为空,右孩子非空)elseparent->_right = cur->_right;//同理,无论删除的是那边,都连接的是右孩子}//删除结点delete cur;}else if (cur->_right == nullptr){//特殊情况是否根节点if (cur == _root){_root = nullptr;}else{if (parent->_left == cur)//删除的是父节点的左边parent->_left = cur->_left;//就让父节点连接删除的结点的左孩子(左孩子非空,右孩子为空)elseparent->_right = cur->_left;//同理,无论删除的是那边,都连接的是左孩子}//删除结点delete cur;}else{//第一种情况-找左子树的最右节点Node* leftRightMost = cur->_left;parent = cur;//此时下面用到的parent都为leftRightMost的父节点while (leftRightMost->_right)//查找最右节点{parent = leftRightMost;leftRightMost = leftRightMost->_right;}//交换swap(cur->_data, leftRightMost->_data);//删除最右节点if (parent->_left == leftRightMost)parent->_left = leftRightMost->_left;//最右节点肯定没有右孩子elseparent->_right = leftRightMost->_left;delete leftRightMost;}return true;}

测试:

具有key-value的二叉搜索树

#include <iostream>
#include <time.h>
using namespace std;template <class K, class V>
struct BNode
{typedef BNode<K, V> Node;K _key; //V _value;Node* _left;//左孩子Node* _right;//右孩子BNode(const K& key, const V& value):_key(key),_value(value), _left(nullptr), _right(nullptr){}
};template <class K, class V>
class BTree
{public:typedef BNode<K, V> Node;BTree():_root(nullptr){}Node* find(const K& key){Node* cur = _root;while (cur){if (cur->_key == key)return cur;else if (cur->_key > key)cur = cur->_left;elsecur = cur->_right;}//平均性能logNreturn cur;}//不插入重复的值bool insert(const K& key, const V& value){if (_root == nullptr){_root = new Node(key, value);return true;}Node* cur = _root;Node* parent = nullptr;while (cur){parent = cur;if (cur->_key == key)return false;else if (cur->_key > key)cur = cur->_left;elsecur = cur->_right;}cur = new Node(key, value);if (parent->_key > key)parent->_left = cur;elseparent->_right = cur;return true;}void inorder(){_inorder(_root);cout << endl;}void _inorder(Node* root){if (root){_inorder(root->_left);cout << root->_key << "-->" << root->_value <<" ";_inorder(root->_right);}}//拷贝二叉搜索树的数据和结构Node* copy(Node* root){if (root == nullptr)return nullptr;Node* newNode = new Node(root->_key, root->_value);newNode->_left = copy(root->_left);newNode->_right = copy(root->_right);return newNode;}BTree(const BTree<K, V>& btree):_root(copy(btree._root)){}void destroy(Node* root){if (root){destroy(root->_left);destroy(root->_right);cout << "destory:" << root->_key << "-->" << root->_value << endl;delete root;}}~BTree(){if (_root){destroy(_root);_root = nullptr;}}bool erase(const K& key){Node* cur = _root;Node* parent = nullptr;while (cur){if (cur->_key == key)break;else if (cur->_key > key){parent = cur;cur = cur->_left;}else{parent = cur;cur = cur->_right;}}//判断是否找到了需要删除的结点if (cur == nullptr)return false;//未找到数据//删除结点//1、删除的结点为叶子结点if (cur->_left == nullptr && cur->_right == nullptr){//特殊情况是否根节点if (cur == _root){_root = nullptr;}else{//需要判断当前节点的位置if (parent->_left == cur)parent->_left = nullptr;elseparent->_right = nullptr;}//删除结点delete cur;}//2.存在一个孩子else if (cur->_left == nullptr)//左孩子为空,右孩子非空{//特殊情况是否根节点if (cur == _root){_root = nullptr;}else{if (parent->_left == cur)//删除的是父节点的左边parent->_left = cur->_right;//就让父节点连接删除的结点的右孩子(左孩子为空,右孩子非空)elseparent->_right = cur->_right;//同理,无论删除的是那边,都连接的是右孩子}//删除结点delete cur;}else if (cur->_right == nullptr){//特殊情况是否根节点if (cur == _root){_root = nullptr;}else{if (parent->_left == cur)//删除的是父节点的左边parent->_left = cur->_left;//就让父节点连接删除的结点的左孩子(左孩子非空,右孩子为空)elseparent->_right = cur->_left;//同理,无论删除的是那边,都连接的是左孩子}//删除结点delete cur;}else{//第一种情况-找左子树的最右节点Node* leftRightMost = cur->_left;parent = cur;//此时下面用到的parent都为leftRightMost的父节点while (leftRightMost->_right)//查找最右节点{parent = leftRightMost;leftRightMost = leftRightMost->_right;}//交换swap(cur->_key, leftRightMost->_key);swap(cur->_value, leftRightMost->_value);//删除最右节点if (parent->_left == leftRightMost)parent->_left = leftRightMost->_left;//最右节点肯定没有右孩子elseparent->_right = leftRightMost->_left;delete leftRightMost;}return true;}
private:Node* _root;//根
};

数据结构 二叉搜索树BST的实现与应用相关推荐

  1. 23王道数据结构二叉搜索树(BST)算法题(6-11题)总结(伪代码)

    6.判断给定的二叉树是否是二叉排序树 算法思想:中序遍历,一棵树为二叉排序树即左右子树为二叉排序树,且当前根节点和左右子树呈递增序列,对左右子树也是如此判断,显然是个递归过程              ...

  2. 【数据结构与算法】3.二叉搜索树(BST)、c++代码

    二叉搜索树(BST).c++代码 参考 https://blog.csdn.net/c_living/article/details/81021510

  3. 看动画学算法之:二叉搜索树BST

    文章目录 简介 BST的基本性质 BST的构建 BST的搜索 BST的插入 BST的删除 看动画学算法之:二叉搜索树BST 简介 树是类似于链表的数据结构,和链表的线性结构不同的是,树是具有层次结构的 ...

  4. 阿里开发者招聘节 | 面试题02-04:给定一个二叉搜索树(BST),找到树中第K小的节点

    为帮助开发者们提升面试技能.有机会入职阿里,云栖社区特别制作了这个专辑--阿里巴巴资深技术专家们结合多年的工作.面试经验总结提炼而成的面试真题这一次将陆续放出(面试题官方参考答案将在专辑结束后统一汇总 ...

  5. 二叉搜索树BST的学习

    文章目录 二叉搜索树BST 什么是BST? 用BST做什么? 一.BST的特性 BST的特性是什么? 1.[230. 二叉搜索树中第K小的元素](https://leetcode.cn/problem ...

  6. 五.树,二叉树,二叉搜索树(BST)和自平衡二叉搜索树(AVL)

    1.树 树是一种数据结构 比如:目录结构 树是一种可以递归定义的数据结构 树是由n个节点组成的集合: 如果 n=0, 那这是一颗空树 如果 n>0, 那存在1个节点作为树的根节点,其他节点可以分 ...

  7. 二叉搜索树(BST)详解

    数据结构:二叉搜索树(BST) 今天咱们来聊聊二叉搜索树,先从字面上来理解,二叉,指的是有两个分支,左子树和右子树:搜索树,啥意思呢,搜索,是不是就是之前学过dfs和bfs那边学过,对啊,dfs就是深 ...

  8. 数据结构---二叉搜索树

    数据结构-二叉搜索树 原理:参考趣学数据结构 代码: 队列代码: #pragma once #define N 100 #define elemType bstTree* #include<st ...

  9. 二叉搜索树 (BST)

    二叉搜索树 (BST : Binary Search Tree) 又名 二叉查找树 或 二叉排序树. 二叉搜索树: 左孩子的值 一定小于或等于 父结点的值 二叉搜索树: 右孩子的值 一定大于或等于 父 ...

最新文章

  1. 漫谈Httpclient
  2. 清除WIN2000中的Administrator账号密码
  3. leetcode - 343. 整数拆分
  4. 李迟2021年10月知识总结
  5. java的constructor怎么用,Java Constructor getDeclaringClass()用法及代码示例
  6. 在SUM()行数中使用SQL变量导致不可预测结果
  7. AtCoder Beginner Contest 171 B - Mix Juice
  8. 使用java.text包格式化数字和日期
  9. 计算机基础知识——计算机概述
  10. 如何成为嵌入式软件工程师_为什么要成为软件工程师
  11. mindmanager2021思维导图许可密钥V21.0.334最新版免费安装下载
  12. JAVA架构师实战第二章 HTMLCSSJAVASCRIPT
  13. 华为浏览器不能下载linux,H5下载手游页面,华为手机浏览器不兼容
  14. HDU - 1546 Idiomatic Phrases Game
  15. “三高”即通常所说的高血压、高血脂(血脂异常)和高血糖三种病症的总称...
  16. iphone ios 视频采集AVCaptureSessionPresetHigh/Medium/Low分辨率等参数
  17. mysql 法语字符比较_法语比较级如何表达?超全整理
  18. 全排列Permutation
  19. Mac OSX系统下使用DosBox编写汇编
  20. GPU虚拟化技术 GVT-g GVT-s GVT-d AMD NVIDIA

热门文章

  1. 微软python免费课程_微软再推免费在线Python教程 面向数据科学和机器学习初学者...
  2. ipa图片解密 php,ipa文件中的图片获取
  3. php给网页加水印_php实现图片添加水印功能
  4. vfp报表纸张设置_JimuReport积木报表 — API数据源报表制作
  5. 简单扫一下官文 - 11G Release2 新特性
  6. Spring Boot 2 快速教程:WebFlux 集成 Thymeleaf 、 Mongodb 实践(六)
  7. Spring3集成Swagger2遇到问题总结
  8. 基于JAVA+SpringMVC+Mybatis+MYSQL的高校学生健康档案管理系统
  9. spring事务和mysql的隔离级别
  10. 短信语音验证码发展的必然趋势