目录

前言

二叉搜索树的概念

二叉搜索树的操作

树的节点实现

搜索树的基本结构

插入数据

查找

删除

拷贝构造函数

二叉搜索树的应用


前言

在c++中的容器里map和set的学习需要二叉搜索树的铺垫,也为后边的的红黑树和AVL树做铺垫,也就是说,今天主要讲搜索树的基本结构和应用。

二叉搜索树的概念

所有的根节点大于左子树的节点,小于右子树的节点的二叉树就叫做二叉搜索树。

二叉搜索的性质:

  • 如果左子树不为空,则左子树上的所有节点都小于根节点。
  • 如果右子树不为空,则右子树上的所有节点都大于根节点。
  • 左右子树也为搜索二叉树

假设我们要查找8,我们从根节点开始查找,这该怎样查找呢?

首先我们先与根节点5对比,发现比根节点5大,所以我们就到它的右子树查找,根节点就变为7,然后我们再与根节点7对比,发现比根节点大,再到根节点7的右子树查找,根节点就变为8,找到该节点之后我们在返回。

二叉搜索树的搜索的时间复杂度

插入和删除操作都必须先查找,查找效率代表了二叉搜索树中各个操作的性能。

对于同一个元素的集合,如果元素的插入顺序不同的,则会出现不同结构的二叉搜索树,也就是说树的结构与元素插入的顺序有关。

搜索效率最优的情况下:完全二叉树,其平均比较的次数是O (logN)

搜索效率最坏的情况:单支二叉树,其平均的比较次数是O(N) 

所以搜索二叉树的搜索的时间复杂度是O(N),时间复杂度看的是最坏的情况。

树的搜索次数与树的层次有关.

二叉搜索树的操作

 树的节点实现

template<class T>
struct TreeNode
{TreeNode* _left;TreeNode* _right;T _key;TreeNode(const T& x):_key(x),_left(nullptr),_right(nullptr){}
};

 搜索树的基本结构

template<class T>
class BinarySearchTree
{public:typedef  TreeNode<T> Node ;//需要构造函数将根节点初始化为空BinarySearchTree():_root(nullptr){}.....private://为什么是定义Node变量?//1.插入删除时,好判断该节点是否为空。//2.节点的左右孩子都是指针,方便赋值Node* _root;//根节点
};

插入数据

a.树为空,则直接插入

b.树不为空,按二叉树搜索的性质插入的位置,插入的位置都会是叶子节点。

c.如果插入的数据在树中已存在,则不将数据插入到树中,并返回false。搜索树的数据都是独一无二的。

代码实现:

非递归版本

 bool _Insert( const T& key){//如果树中已存在key,则返回falseif (_root == nullptr){_root = new Node(key);return true;}//遍历搜索树Node* cur = _root;Node* parent = nullptr;while (cur){if (cur->_key<key){parent = cur;cur = cur->_right;}else if (cur->_key > key){parent = cur;cur = cur->_left;}else{return false;}}//创建节点,这里用了匿名对象cur = new Node(key);//判断cur节点要连接到parent的哪个节点if (parent->_key > key){parent->_left = cur;}else if (parent->_key < key){parent->_right = cur;}return true;}

递归的版本:

 //注意root是指针的引用bool _InsertR(Node* &root, const T& key){//如果节点为空,则创建节点给给它if (root == nullptr){root = new Node(key);}if (root->_key > key){_InsertR(root->_left, key);}else if(root->_key<key){_InsertR(root->_right, key);}else {return false;}return true;}bool InsertR(const T& key){return _InsertR(_root, key);}

_Insert是在private里面的,而Insert是public里的。

查找

找到了就返回该节点的地址,找不到就返回空。

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

删除

当我们要删除搜索树中某个节点的时候,删除后的树的结构也需要保持着搜索的性质,所以删除有有以下4种情况、

  • 要删除的节点无孩子节点。例如 0 4 6 9

只要我们找到该值,直接删除掉就行。

  • 要删除的节点有左孩子的节点。例如 1

找到该值,先让该值的父亲节点区连接它的左孩子节点,然后再删除掉。父亲节点要连接该值的左孩子节点时,需要判断孩子节点是连接在父亲的左边还是右边

  • 要删除的节点有右孩子的节点。例如 8

找到该值,先让该值的父亲节点区连接它的右孩子节点,然后再删除掉。父亲节点要连接该值的右孩子节点时,需要判断孩子节点是连接在父亲的左边还是右边

  • 要删除的节点有左右孩子的节点。例如 3 5 7

如果我们直接删除这样的节点时,就会让使它的左右子树没有根节点,所以我们怎样删除这样的节点,并保持搜索二叉树的特性?

我们可以找出该值左子树最大的节点或者右子树最小的节点,去替换根节点,然后将替换的节点给删除掉。(左子树最大的节点或者右子树最小的节点去替换的根节点依旧可以保持着搜索二叉树的性质)。

删除5这个数据的过程;,找出左子树最大的值,也就是左子树最右的值,替换到根节点,然后直接将 替换的节点删除即可。

删除3的过程,找出左子树最大的值,也就是左子树最右的值。

代码:

 bool _erase(const T key){if (_root == nullptr)return false;//找出key的节点Node* cur = _root;Node* parent = nullptr;while (cur){if (cur->_key < key){parent = cur;cur = cur->_right;}else if (cur->_key > key){parent = cur;cur = cur->_left;}else {break;}}//第一种:删掉的是叶子节点//第二种:删掉只有一个孩子的节点//第三种:删掉的是有两个孩子的节点//删除只有右孩子的节点if (cur->_left == nullptr){if (parent==nullptr){_root = cur->_right;delete cur;return true;}if (parent->_left == cur){parent->_left = cur->_right;}else if (parent->_right == cur){parent->_right = cur->_right;}delete cur;}//删除只有左孩子的节点else if (cur->_right == nullptr){if (parent == nullptr){_root = cur->_left;delete cur;return true;}if (parent->_left == cur){parent->_left = cur->_left;}else if (parent->_right == cur){parent->_right = cur->_left;}delete cur;}//删除有左右孩子的节点else{//第三种情况Node* leftmax = cur->_left;Node* leftmaxparent = cur;//左子树最大节点//找出左子树最大的节点while (leftmax->_right){leftmaxparent = leftmax;leftmax = leftmax->_right;}//赋给根节点cur->_key = leftmax->_key;//删除节点if (leftmaxparent->_left == leftmax){leftmaxparent->_left = leftmax->_left;}else {leftmaxparent->_right = leftmax->_left;}delete leftmax;return true;}return false;}

这段代码的含义:

递归版本:

 bool _eraseR(Node* &root,const T key){//查找key相对应的值if (root == nullptr)return false;if (root->_key < key)_eraseR(root->_right, key);else if (root->_key > key)_eraseR(root->_left, key);else {if (root->_left == nullptr){Node* tmp = root;root = root->_right;delete tmp;}else if (root->_right == nullptr){Node* tmp = root;root = root->_left;delete tmp;}else{//第三种情况Node* leftmax = root->_left;Node* leftmaxparent = root;//找出左子树最大的节点while (leftmax->_right){leftmaxparent = leftmax;leftmax = leftmax->_right;}//赋给根节点root->_key = leftmax->_key;//删除节点if (leftmaxparent->_left == leftmax){leftmaxparent->_left = leftmax->_left;}else {leftmaxparent->_right = leftmax->_left;}delete leftmax;return true;}}return false;}bool eraseR(const T& x){return _eraseR(_root, x);}

_eraseR是private里面的,eraseR是public里面的。

eraseR是对_eraseR的封装,方便被调用。

拷贝构造函数

Node* _copy(Node* root){if (root == nullptr)return nullptr;Node* copyroot = new Node(root->_key);copyroot->_left=_copy(root->_left);copyroot->_right=_copy(root->_right);return copyroot;    }BinarySearchTree(BinarySearchTree<T>& t){_root=_copy(t._root);}

赋值重载函数

析构函数:

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

2.4 二叉搜索树的应用

1.K模型:K模型只有key作为关键码,也就是定义树的节点只需要存_key一个即可,关键码即为搜索到的值。如上我们写的搜索树就是k模型,因为树的节点就只有一个key可以存储数据

比如:我们要进学校的图书馆就需要刷我们的学生卡,通过磁感应将门打开。如下:

此时我们就需要先将学生的学号录入到该门的管理系统,然后将数据建立起搜索树,我们的学号在学校是唯一的,当我们刷卡的时候,就会去管理系统快速查找我们的学号,找到了门就开了。

k模型在实际应用中就是就是查找在不在的问题

2.kv模型:每一个关键码key都有一个对应的value值,也就是说树的节点中有两个值,一个是key,一个是value的值,我们通过key找到对应的节点,然后将相对应的value给映射出来。也就是说,我们增删查改的时候也是按key进行插入或者删除。只是创建节点,增加了一个value值。

场景1:我们去网上买高铁票,每张高铁票的信息都有与一个身份证号绑定在一起的,当我们进高铁站都需要刷身份证在搜索树中查找有没有你的身份证号,找到了,通过身份证号映射你的高铁信息,看看有没有你的班次,门才是否可以被开。在这里_key是身份证,它是独一无二的,_value是高铁信息,通过身份证将它给映射出来。

场景2:

英汉词典就是英文与中文的对应关系,我们只需要查找有没有该英文,找到了,就通过映射关系,将该英文的单词的中文给映射出来。

用kv模型的搜索树写一个简单的词典:

int main()
{BinarySearchTree<string ,string> v;v._Insert("string","字符串");v._Insert("apple","苹果");v._Insert("automate", "n.自动化,v.使自动化");v._Insert("voluntary", "adj.自愿的主动的,n.志愿者");string s;while (cin>>s){struct TreeNode<string,string>* ret = v.find(s);if (ret == nullptr){cout << "没有找到该单词" << endl;}else{cout <<endl << "   中文:" << ret->_value << endl;}}return 0;
}

运行结果:

场景3:

统计水果次数。

如下

int main()
{const string str[] = { "苹果","西瓜","芒果","苹果","西瓜","西瓜","西瓜","苹果","芒果" };BinarySearchTree<const string, int> count;for (auto s : str){auto ret = count.find(s);if (ret == nullptr){count._Insert(s, 1);}else {ret->_value++;}}count.Inorder();return 0;
}

输出结果:

其中Inorder是将树中所有的节点都给打印出来,代码如下:

 void _Inorder(Node* root){if (root == nullptr)return;_Inorder(root->_left);cout << root->_key << ":" << root->_value << endl;_Inorder(root->_right);}void Inorder(){_Inorder(_root);}

_Inorder是private里面的,而Inorder是public里面的。

好了,今天分享的知识就到这里了,喜欢的小伙伴们,麻烦帮我点个三连,谢谢你们了。

【数据结构】——二叉搜索树相关推荐

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

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

  2. 二叉搜索树的删除操作可以交换吗_JavaScript数据结构 — 二叉搜索树(BST)ES6实现...

    1. 概述 最基本的数据结构是向量和链表,为了将二者的优势结合起来,我们引入了二叉树,可以认为二叉树是列表在维度上的拓展.而今天要介绍的二叉搜索树(BST)则是在形式上借鉴了二叉树,同时也巧妙借鉴了有 ...

  3. 数据结构 二叉搜索树BST的实现与应用

    概念 二叉查找树(Binary Search Tree),(又:二叉搜索树,二叉排序树)它或者是一棵空树,或者是具有下列性质的二叉树: 1.若它的左子树不空,则左子树上所有结点的值均小于它的根结点的值 ...

  4. [学习][数据结构]二叉搜索树

    定义 一棵二叉搜索树是以一棵二叉树来组织的,如下图.这样一棵树可以使用一个链表数据结构来表示,其中每个节点就是一个对象.除了key和卫星数据之外,每个节点还包含属性left.right和p,他们分别指 ...

  5. 数据结构——二叉搜索树

    一.定义 二叉搜索树(binary search tree),又叫二叉查找树.二叉排序树.若它的左子树不空,则左子树上所有结点的值均小于它的根结点的值: 若它的右子树不空,则右子树上所有结点的值均大于 ...

  6. 数据结构——二叉搜索树的C语言实现

    1.什么是二叉搜索树? 2.二叉搜索树的操作 3.二叉搜索树的C语言实现 #include<stdio.h> #include<stdlib.h>#define Element ...

  7. 数据结构 二叉搜索树的删除

    文章目录 概述 待删除的结点没有子树 待删除的结点仅有一颗子树 待删除的结点有两颗子树 C代码实现 概述 这是一篇短文,专门考究一下二叉搜索树的删除. 二叉搜索树的建立非常简单,如果不熟悉的见此文 树 ...

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

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

  9. 【ACM】二叉搜索树(Binary Search Tree /BS Tree) 小结

    动态管理集合的数据结构--二叉搜索树 搜索树是一种可以进行插入,搜索,删除等操作的数据结构,可以用字典或者优先队列. 二叉排序树又称为二叉查找树,他或者为空树,或者是满足如下性质的二叉树. (1)若它 ...

  10. 【LeetCode笔记】96. 不同的二叉搜索树(Java、动态规划)

    文章目录 题目描述 代码 & 思路 精简版 2.0 题目描述 这道题其实不用构造数据结构 二叉搜索树:只要利用这个结构的性质即可,即:左右两子,左小右大 然后用动态规划来做,具体如何推导见思路 ...

最新文章

  1. 二:serverless网站数据库操作
  2. fail-fast和fail-safe的介绍和区别
  3. 熬了一晚上,我从零实现了Transformer模型,把代码讲给你听
  4. python进程间通信的秘密
  5. 简单有趣的 NLP 教程:手把手教你用 PyTorch 辨别自然语言(附代码)
  6. Python语言学习:python语言的特点、入门、基础用法之详细攻略
  7. VTK:图片之ImageLaplacian
  8. linux安装mysql8依赖的环境_CentOS Linux release 8 安装mysql8.
  9. 上古时期(大雾)的数据结构pdf
  10. centos 搭建Jenkins
  11. Excel 如何让复制的内容保持原文本格式
  12. GIS数据下载资源大全
  13. 在Excel表格中隐藏行或列
  14. 转载:.Net 程序集 签名工具sn.exe 密钥对SNK文件 最基本的用法
  15. [NOIP 2009] 细胞分裂
  16. GPT分区是什么?如何创建GPT分区
  17. 使用Python开发一个恐龙跑跑小游戏,玩起来
  18. 一个好用的项目工时管理系统
  19. 应急响应 - Windows启动项分析,Windows计划任务分析,Windows服务分析
  20. MyBatis07:使用注解开发

热门文章

  1. 通过国家认定的在线监测仪器_在线监测_环境保护网
  2. AI人工智能标记数据的技术:类型、方法、质量控制、应用
  3. 童年那些牛B的顺口溜 【转】
  4. □ 影片名:《木乃伊》(5156) 在线播放
  5. 2021-2027中国半导体掩膜版市场现状及未来发展趋势
  6. 影响几代人的《芳华》,是如何创造14亿票房的?
  7. 如何在EXCEL中的度分秒格式的经纬度转换成度格式的数据
  8. 【技术新趋势】合合信息:文本纠错提升OCR任务准确率的方法
  9. VMware虚拟机屏幕无法上下移动,导致无法查看到ip地址的解决办法
  10. 高清地图数据获得方法