前言

学习了一些数据结构之后,你是不是已经有些小得意了,以为数据结构就这点东西嘛,不就用好栈、队列什么什么的就好,呵呵,那些只是皮毛。接下来的东西才会让你真正认识到数据结构的博大精深,当你看到接下来那些酷炫狂赛的操作时,你会惊叹于数据结构的神奇。在学习那些酷炫的AVL树,红黑树之前,我们先入个门,学习最简单的动态查找表——二叉查找树。

二叉查找树

二叉查找树有被称为二叉排序树,它要么是个空树,要么就满足下列条件:

  1. 若左子树不空,则左子树中所有元素的值比根节点小
  2. 若右子树不空,则左子树中所有元素的值比根节点大
  3. 二叉查找树的左右子树都是二叉查找树(疯狂暗示递归实现)

如图,便是一个二叉查找树

基本实现

存储实现:

二叉查找树就使用链表实现,这样能够很好的理解,每个节点有一个元素存储值,两个指针分别指向它的左子树和右子树。

template<class elemType>
class binarySearchTree
{
private:struct node{elemType data;node *left;node *right;node(const elemType &d, node *ln = NULL, node *rn = NULL):data(d), left(ln), right(rn){}};node *root;
}

find函数实现:

查找操作从根节点开始,一个一个节点递归查找。对于一个特定节点,就四个步骤:

  • 如果该节点是NULL,那么说明已经查找完了整个树,还没有找到。
  • 若该节点的值与要找的值吻合,那么找到,退出
  • 若要找的值大于该节点的值,查找它的右子树节点
  • 若要找的值小于该节点的值,查找它的左子树节点
    elemType *find(const elemType &x){return find(x, root);}elemType *find(const elemType &x, node *t){if(t == NULL){cout << "Can not find" << endl;return 0;}if(t -> data == x){return &(t -> data);}if(x < t -> data){return find(x, t -> left);}else{return find(x, t -> right);}}

midOrder操作:

中序遍历输出整个树的值,细心的人其实已经从二叉查找树做小右大的性质想到了,二叉查找树中序遍历的结果必然是一个从小到大序列啊。

    void midOrder(){midOrder(root);}void midOrder(node *p){if(p == NULL){return;}midOrder(p -> left);cout << p -> data << ' ';midOrder(p -> right);}

insert操作:

insert操作插入的一定是叶节点

记住这一点,插入的时候一个一个比较就好,从根节点开始,大就向右,小就向左,直到最后的NULL,在NULL处插入节点就好。

    void insert(const elemType &x){insert(x, root);}void insert(const elemType &x, node *&t){if(t == NULL){t = new node(x, NULL, NULL);}else{if(x < t -> data){insert(x, t -> left);}else{if(x == t -> data){cout << "The node has existed" << endl;return;}else{insert(x, t -> right);}}}}

remove操作:

remove操作是二叉查找树中最难的,但你也不要害怕,毕竟你都已经看到这儿了对吧,不看完怎么行。

想想remove其实就分三种情况:

  • remove的节点是叶节点,那么二话不说直接删了就好
  • remove的节点有一个左子树或者有一个右子树,那么直接把这个节点删去,它的子树补上来就好
  • remove的节点有左子树右子树都有,这就比较麻烦了,是不是已经蒙圈了,不知道怎么补了,不要慌,且待我慢慢给你讲解:删除了这个节点,这个位置又不能空着,所以我们按正常人的思维就找了个替身来填补这个位置,那么怎样选择这个替身才能保证树仍保持有序呢?静下心来想想,要想保持有序,那必须补上来左子树的最大值或是右子树的最小值啊,其实就是中序遍历的该值的相邻两个值是吧。
    找左子树的最大值,其实就是从左子树的根节点开始,一直往右找,直到末端,那末端的值必然是左子树的最大值了,同理,找右子树的最小值,其实就是从右子树的根节点开始,一直往左找,直到末端,那末端的值必然是右子树的最小值了。(代码以找右子树的最小值为例写的)
    找到了替身,使要删节点的值改为替身的值,然后删掉替身就好(有没有一种恩将仇报的赶脚),这里的删掉替身的操作一定是满足前两种操作的。

    void remove(const elemType &x){remove(x, root);}void remove(const elemType &x, node *&t){//注意这里是指针的引用,不是复制构造,因为要满    //足补上来子树与上面的节点能够接上if(t == NULL){return;}if(x < t -> data){remove(x, t -> left);}else{if(x > t -> data){remove(x, t -> right);}else{                        //==if(t -> left != NULL && t -> right != NULL){node *tmp = t -> right;while(tmp -> left != NULL){tmp = tmp -> left;}t -> data = tmp -> data;remove(t -> data, t -> right);}else{node *old = t;if(t -> left == NULL && t -> right == NULL){delete old;}else{if(t -> left!= NULL){t = t -> left;}else{t = t -> right;}delete old;}}}}}

完整代码:

#include <iostream>using namespace std;template<class elemType>
class binarySearchTree
{
private:struct node{elemType data;node *left;node *right;node(const elemType &d, node *ln = NULL, node *rn = NULL):data(d), left(ln), right(rn){}};node *root;public:binarySearchTree(){root = NULL;}~binarySearchTree(){clear(root);}void clear(node *t){if(t == NULL){return;}clear(t -> left);clear(t -> right);delete t;}elemType *find(const elemType &x){return find(x, root);}elemType *find(const elemType &x, node *t){if(t == NULL){cout << "Can not find" << endl;return 0;}if(t -> data == x){cout << "haha" << endl;return &(t -> data);}if(x < t -> data){return find(x, t -> left);}else{return find(x, t -> right);}}void insert(const elemType &x){insert(x, root);}void insert(const elemType &x, node *&t){if(t == NULL){t = new node(x, NULL, NULL);}else{if(x < t -> data){insert(x, t -> left);}else{if(x == t -> data){cout << "The node has existed" << endl;return;}else{insert(x, t -> right);}}}}void remove(const elemType &x){remove(x, root);}void remove(const elemType &x, node *&t){if(t == NULL){return;}if(x < t -> data){remove(x, t -> left);}else{if(x > t -> data){remove(x, t -> right);}else{   //==if(t -> left != NULL && t -> right != NULL){node *tmp = t -> right;while(tmp -> left != NULL){tmp = tmp -> left;}t -> data = tmp -> data;remove(t -> data, t -> right);}else{node *old = t;if(t -> left == NULL && t -> right == NULL){delete old;}else{if(t -> left!= NULL){t = t -> left;}else{t = t -> right;}delete old;}}}}}void midOrder(){midOrder(root);}void midOrder(node *p){if(p == NULL){return;}midOrder(p -> left);cout << p -> data << ' ';midOrder(p -> right);}};

总结

二叉查找树性能与结构有很大关系,如果构建的好,也就是二叉查找树接近于一棵完全二叉树,那么其所有操作都是O(logn)的,但如果数据很变态,刚好形成了一条链,那就是线性O(n)了。

不过总归是有办法解决的,那就是传说中的AVL树和红黑树,之后会慢慢讲到。

二叉查找树的原理及实现相关推荐

  1. 数据结构与算法--二叉查找树实现原理

    二叉查找树 二叉树的一个重要应用就是他在查询中的使用,假设书中每个节点存储一项数据.在我们的案例中,任意复杂的项在java中都容易处理,但为了简单还是假设都是整数.还假设他们都是不重复的整数,使二叉树 ...

  2. 5.什么是二叉查找树?原理

    二叉查找树:又叫二叉搜索树.二叉排序树.它或是一颗空树,如果不是空树,它的左子树不为空,它的左子树上的任何节点都小于根节点.它的右子树不为空,则它的右子树所有的节点均大于根节点.并且左子树和右子树均为 ...

  3. 数据结构与算法--二叉查找树转顺序排列双向链表

    二叉查找树转顺序排列双向链表 题目:输入一颗二叉查找树,将二叉查找树转成一个排序的双向链表,要求不能创建任何新节点,只调整树节点中指针的指向.例如下图所示: 本次二叉查找树节点定义使用之前文章 数据结 ...

  4. 数据结构与算法--面试必问AVL树原理及实现

    数据结构与算法–AVL树原理及实现 AVL(Adelson-Velskii 和landis)树是带有平衡条件的二叉查找树,这个平衡条件必须容易实现,并且保证树的深度必须是O(logN).因此我们让一棵 ...

  5. 3分钟火速手写一个二叉查找树,搞快点。

    文章目录 定义 实现 完整代码 测试 问题 因为二叉查找树的原理和实现比较简单,所以我们简单了解一下最基本的概念,就直接开始写代码. 定义 二叉查找树(Binary Search Tree,BST), ...

  6. 数据结构与算法--死磕二叉树

    死磕二叉树 近一年都比较关注算法相关的知识,也刷了不少题,之前的文章中大多也是算法相关的文章,但是感觉每次遇到树相关的题型都不能应对自如,因此还是有必要在相关知识上下功夫,因此有此次总结,以下是所有树 ...

  7. 数据结构与算法--力扣109题将有序双向链表转换为二叉搜索树

    将有序数组转换为二叉搜索树 近一年都比较关注算法相关的知识,也刷了不少题,之前的文章中大多也是算法相关的文章,但是感觉每次遇到树相关的题型都不能应对自如,因此还是有必要在相关知识上下功夫,因此有此次总 ...

  8. 数据结构与算法--力扣108题将有序数组转换为二叉搜索树

    力扣108提将有序数组转换为二叉搜索树 近一年都比较关注算法相关的知识,也刷了不少题,之前的文章中大多也是算法相关的文章,但是感觉每次遇到树相关的题型都不能应对自如,因此还是有必要在相关知识上下功夫, ...

  9. 数据结构与算法--求1~n能组成的所有二叉搜索树的排列

    给定一个整数n,生成并返回所有N个节点组成并且节点值从1到n互不相同的不同二叉树,可以按照任意顺序 二叉树文章列表: 数据结构与算法–面试必问AVL树原理及实现 数据结构与算法–二叉树的深度问题 数据 ...

最新文章

  1. 深入理解 Java 虚拟机(第二弹) - 常用 vm 参数分析
  2. MQCache 秒开缓存快速入门指南 - 旁路(使用镜像交换机)
  3. ural 1014. Product of Digits
  4. UA MATH565C 随机微分方程VI 扩散过程简介
  5. linux添加怎么退出,linux – 是否可以设置’expect’的退出代码
  6. R中统计假设检验总结(一)
  7. hadoop版本升级到2.4.1
  8. Qt4_用QPainter绘图
  9. w7电脑蓝屏怎么解决_电脑蓝屏Win32k.sys怎么办
  10. BZOJ4003 [JLOI2015]城池攻占 左偏树 可并堆
  11. SQL Sever — 建表语句,设置种子数量与增量以及设置主键 代码
  12. struts2拦截器
  13. CacheCloud-资源归档
  14. 2019莆田学院c语言试卷,莆田学院C语言程序设计模拟试卷_文库吧
  15. Emacs footnote 自动排序
  16. 那些中了500万的人过上想要的生活了吗
  17. 计算机内存不足黑屏怎么办,win10内存不足会黑屏吗_win10电脑内存不足黑屏了怎么办...
  18. 网络协议 -- IP地址
  19. 学习笔记 JavaScript 动画 加速
  20. 网络视频无法快进无法选中进度条

热门文章

  1. 基于FRR全面解析BGP协议(五):FRR的BGP路由策略
  2. 代谢组学分享-花青素通过调节氨基酸代谢改善糖尿病肾病的肾功能
  3. Alibaba开源UI框架V-Layout
  4. 化工贸易行业的客户信用及应收账款管理解决方案
  5. 策略构建:均值回归模型
  6. [java] JDK9常用特性
  7. 详解FIR滤波器和IIR滤波器的区别
  8. 腾讯云centOS7服务器 防火墙及端口常用命令
  9. 智能健身镜中的晶振应用
  10. Linux 服务器配置网卡绑定(bonding)详解