二叉搜索树(BST)学习笔记
BST调了一天,最后遍历参数错了,没药救了……
本文所有代码均使用数组+结构体,不使用指针!
前言——BST是啥
BST 二叉搜索树是基于二叉树的一种树,一种特殊的二叉树。
二叉搜索树要么是一颗空树,要么满足一下特点(性质)的二叉树:
- 它的左子树要么为空,要么它(左子树)的所有节点均小于它的根节点。
- 它的右子树要么为空,要么它(右子树)的所有节点均大于它的根节点。
- 它的左、右子树也分别是二叉搜索树。
直观的说,如果中序遍历一棵二叉搜索树,则会产生一个有序数列。
如:,中序遍历会产生序列:1 2 5 6 8 9
No.1——算法复杂度分析
二叉搜索树是"排过序"的二叉树,并非"用于排序"的二叉树。
它的优势在于"有序性",而且其插入和查找的时间复杂度均为\(O(h)\),一般情况下\(h=O(\log_{2}n)\),n代表节点数,h代表树的高度。堆的插入算法虽然时间复杂度为\(O(\log_{2}n)\),但并不具有有序性。
No.2——使用范围
对比上述的的分析,发现BST的特点是:
- 有序
- 插入、查找等算法高效
因此,BST使用范围是:要经常对有序数列进行"动态的"插入或查找等工作。
No.3——基本操作
BST的基本操作很多,一时半会也讲不过来,就从易到难的讲吧。
No.3-1——三种遍历方式
BST的遍历与二叉树和树一样,有三种:先序遍历、中序遍历、后续遍历。
三种遍历的方式:
- 先序遍历:根→左→右 (DLR)
- 中序遍历:左→根→右 (LDR) (结合定义,想一想,为什么中序遍历就是有序的??)
- 后续遍历:左→右→根 (LRD)
想必大家都知道了吧,上代码。
/*========遍历========*/
void bl(int how,int now)
{
if(how==1){ //先序遍历
cout<<a[now].data<<" ";
if(a[now].l!=-1)
bl(1,a[now].l);
if(a[now].r!=-1)
bl(1,a[now].r);
}
if(how==2){ //中序遍历
if(a[now].l!=-1)
bl(2,a[now].l);
cout<<a[now].data<<" ";
if(a[now].r!=-1)
bl(2,a[now].r);
}
if(how==3){ //后续遍历
if(a[now].l!=-1)
bl(3,a[now].l);
if(a[now].r!=-1)
bl(3,a[now].r);
cout<<a[now].data<<" ";
}
}
No.3-2——建树与插入
强烈建议使用父亲孩子表示法!!!
所谓建树,就是构建一颗树,建树的时候必然涉及到插入。
也没有什么好说的,根据定义走,他怎么说,你怎么做。
- 第一个为根节点
- 比根大,往右对比
- 比根小,往左对比
- 如果当前为空,插入成功。
就这四步,代码来了
/*========插入========*/
void into(int sum,int now,int tot)
{
if(sum<a[now].data)
if(a[now].l!=-1)
into(sum,a[now].l,tot);
else {
a[now].l=tot;
a[a[now].l].data=sum;
a[a[now].l].fa=now;
}
else if(sum>a[now].data)
if(a[now].r!=-1)
into(sum,a[now].r,tot);
else {
a[now].r=tot;
a[a[now].r].data=sum;
a[a[now].r].fa=now;
}
}
/*========构建========*/
void init()
{
cin>>n;
int x,tot;
for(tot=1;tot<=n;tot++){
scanf("%d",&x);
if(tot==1)
a[tot].data=x;
else
into(x,1,tot);
}
}
No.3-3——查找
查找也是二叉搜索树必不可少的一个操作,我这里find()
返回了是第几个数,方便删除。
实现很简单,只用熟练掌握二叉搜索树的性质,便可轻易打出以下代码:
/*========查找========*/
int find(int now,int sum)
{
cout<<now<<endl;
if(a[now].data==sum) return now;
if(sum<a[now].data)
if(a[now].l!=-1) return find(a[now].l,sum);
else return 0;
else if(sum>a[now].data)
if(a[now].r!=-1) return find(a[now].r,sum);
else return 0;
}
void init2()
{
cin>>n;
int i,x;
for(i=1;i<=n;i++){
cin>>x;
if(find(ROOT,x)) printf("Yes\n");
else printf("No\n");
}
}
No.3-4——求前驱后继
前驱后继就是在中序遍历时他的前一个与后一个,如图:
如图,八的前驱是六,后继是九。
这太简单了!
放到树上说,前驱就是该节点左儿子的最右节点,后继则是该节点右儿子的最左节点。
放一个前驱的代码,后继自己推!
/*========前驱========*/
int pred(int now)
{
if(a[now].r!=-1) return pred(a[now].r);
else return now;
}
No.3-5——删除
本操作有一定难度,请务必弄懂
删除要分一些情况讨论,见下:
- 没有孩子
- 只有一个孩子
3-1. 有两个孩子
3-2. 删除根
第一种情况好办,删掉就行了。
第二种情况也行,删掉后接上左/右孩子。
第三种情况有些复杂,需要用到前驱/后继(用哪个没有一定要求),用它的前驱/后继代替它,同时删除(一个递归过程,又一次调用删除函数)它的前驱/后继。
看一组图吧(有点大):
这是一颗二叉搜索树,我们要删掉3。
找到前驱。
替换。
最后变成这个样子。
最后讲一讲如何删根节点,其实与其他删除差不多,只需在开个ROOT
变量存储根是谁就行了(根默认为1)
因为一些原因,不放代码。(其实是我太懒了)
No.4——写在最后
其实个人认为二叉搜索树的实用价值不大,主要用于练手与预备知识,比方说Treap、平衡树、堆……都需要用到二叉搜索树的性质。
综合代码算了,删除你们自己打吧!
No.4-1——练习题
但,算法之路,才刚刚开始!
二叉搜索树(BST)学习笔记的更多相关文章
- 萌新笔记之二叉搜索树(BST)
前言,以前搞过线段树,二叉树觉得也就那样= =.然后数据结构的课也没怎么听过,然后下周期中考... 本来以为今天英语考完可以好好搞ACM了,然后这个数据结构期中考感觉会丢人,还是好好学习一波. 二叉搜 ...
- C++版 - 剑指offer 面试题24:二叉搜索树BST的后序遍历序列(的判断) 题解
剑指offer 面试题24:二叉搜索树的后序遍历序列(的判断) 题目:输入一个整数数组,判断该数组是不是某二叉搜索树的后序遍历的结果.如果是则返回true.否则返回false.假设输入的数组的任意两个 ...
- 给定一个二叉搜索树(BST),找到树中第 K 小的节点
问题:给定一个二叉搜索树(BST),找到树中第 K 小的节点. 出题人:阿里巴巴出题专家:文景/阿里云 CDN 资深技术专家. 考察点: 1. 基础数据结构的理解和编码能力 2. 递归使用 参考答案 ...
- 在二叉搜索树(BST)中查找第K个大的结点之非递归实现
一个被广泛使用的面试题: 给定一个二叉搜索树,请找出其中的第K个大的结点. PS:我第一次在面试的时候被问到这个问题而且让我直接在白纸上写的时候,直接蒙圈了,因为没有刷题准备,所以就会有伤害.(面完的 ...
- 二叉搜索树 (BST) 的创建以及遍历
二叉搜索树(Binary Search Tree) : 属于二叉树,其中每个节点都含有一个可以比较的键(如需要可以在键上关联值), 且每个节点的键都大于其左子树中的任意节点而小于右子树的任意节点的键. ...
- [LeetCode] Convert BST to Greater Tree 将二叉搜索树BST转为较大树
Given a Binary Search Tree (BST), convert it to a Greater Tree such that every key of the original B ...
- 二叉搜索树(BST)
(第一段日常扯蛋,大家不要看)这几天就要回家了,osgearth暂时也不想弄了,毕竟不是几天就能弄出来的,所以打算过完年回来再弄.这几天闲着也是闲着,就掏出了之前买的算法导论看了看,把二叉搜索树实现了 ...
- hdu 3791:二叉搜索树(数据结构,二叉搜索树 BST)
二叉搜索树 Time Limit : 2000/1000ms (Java/Other) Memory Limit : 32768/32768K (Java/Other) Total Submiss ...
- 数据结构---二叉搜索树BST实现
1. 二叉查找树 二叉查找树(Binary Search Tree),也称为二叉搜索树.有序二叉树(ordered binary tree)或排序二叉树(sorted binary tree),是指一 ...
随机推荐
- 2018-2019-2 网络对抗技术 20165228 Exp1 PC平台逆向破解
2018-2019-2 网络对抗技术 20165228 Exp1 PC平台逆向破解 实验内容及步骤 第一部分:直接修改程序机器指令,改变程序执行流程 关键:通过修改call指令跳转的地址,将原本指向被 ...
- JavaScript 之 预编译 作用域,作用域链
第一次写博客,本来是学习jQuery遇到闭包问题,发现并没有理解闭包,发现闭包牵扯的知识点太多.复习了一遍(发现自己该记住的全忘了)写在博客里,自己也是小白,希望大神们指点迷津,必将感激不尽. 我们知 ...
- mybatis 学习笔记 -详解mybatis 及实例demo
快速入门1 要点: 首先明白mybatis 是什么 这是一个持久层的框架.之前叫做ibatis.所以,在它的代码中出现ibatis这个词的时候,不要感到惊讶.不是写错了,它确实就是这个样子的. 首先, ...
- css里px em rem特点(转)
1.px特点: 1.IE无法调整px作为单位的字体大小: 2.Firefox能够调整px.em和rem. px是像素,是相对长度单位,是相对于显示器屏幕分辨率而言的. 2.em特点: 1.em的值并不 ...
- 20175202 《Java程序设计》第六周学习总结
20175202 2018-2019-2 <Java程序设计>第六周学习总结 教材知识点总结 1.内部类: Java支持在一个类中定义另一个类,称作内部类,包含内部类的类叫做外嵌类. 内部 ...
- 在mysql配置文件修改sql_mode或sql-mode 怎么办?
很多在安装程序配置数据库这一步中会出现: 请在mysql配置文件修改sql_mode或sql-mode 这个问题处理很简单: mysql中修改my.cnf,找到sql_mode,修改值为: NO_AU ...
- 18.21 关键字extern
用#include可以包含其他头文件中变量.函数的声明,为什么还要extern关键字? 1.头文件 其实头文件对计算机而言没什么作用,只是在预编译时在#include的地方展开一下,没别的意义了.将头 ...
- 【DevExpress】邮箱制作小结
利用DevExpress的RichEditControl控件可以发送包含图片的邮件.但存在一个问题.RichEdit直接将图片解析成base64码包含在RichEdit的HtmlText中,这导致客户 ...
- 嵌入式C语言常见的错误
预处理的错误: #include “stdio.h” //引用符号错误 #inlcude <name> //自定义文件用 " " not find gcc -I ...
- HashTable & HashMap & ConcurrentHashMap 原理与区别
一.三者的区别 HashTable HashMap ConcurrentHashMap 底层数据结构 数组+链表 数组+链表 数组+链表 key可为空 否 是 否 value可为空 否 是 否 ...