『笔记』二叉搜索树
定义
百度百科者云:
二叉查找树 \((Binary Search Tree)\) ,\((\) 又:二叉搜索树,二叉排序树, \(BST)\) 它或者是一棵空树,或者是具有下列性质的二叉树:
若它的左子树不空,则左子树上所有结点的值均小于它的根结点的值; 若它的右子树不空,则右子树上所有结点的值均大于它的根结点的值; 它的左、右子树也分别为二叉排序树。
简单来说:
-
一棵带点权的二叉树
-
左子树上的所有节点权值均小于根节点
-
右子树上的所有节点权值均大于根节点
-
某节点的左右子树同样满足上述性质
二叉搜索树上的基本操作的时间复杂度与这棵树的深度成正比。对于一个有 \(n\) 个结点的二叉搜索树中,这些操作的最优时间复杂度为 \(O(\log n)\) ,最坏为 \(O(n)\) 。随机构造这样一棵二叉搜索树的期望高度为 \(O(\log n)\) 。
功能
二叉搜索树作为一种经典的数据结构,它既有链表的快速插入与删除操作的特点,又有数组快速查找的优势。
所以应用十分广泛,例如:
-
在文件系统和数据库系统一般会采用这种数据结构进行高效率的排序与检索操作
-
这种快速便利
且随机毒瘤易被卡的优(gui)秀(chu)数据结构对于 \(OI\) 作用也是大大滴。
一般的二叉搜索树支持:
-
指定元素的插入和删除
-
查询某元素是否属于某集合、它的最大值、最小值、前驱、后继
-
集合的有序遍历
-
\(\dotsb\)
实现
变量声明
/*=============================================自定义函数*/
const int _ = 1e5 + 10;
struct BST
{
int ls, rs, Val;
} t[_];
int n, siz, root = 1;
int val;
名称 | 含义 |
---|---|
BST | 二叉树(变量不解释) |
siz | 节点个数 |
root | 根节点 |
val | 新的节点权值 |
插入 val
插入分为四种情况:
- 若 \(now\) 为空,直接返回一个值为 \(val\) 的新节点。
-
若 \(now\) 的权值大于 \(val\) ,在 \(now\) 的左子树中插入权值为 \(val\) 的节点。
-
若 \(now\) 的权值小于等于 \(val\) ,在 \(now\) 的右子树中插入权值为 \(val\) 的节点。
void ins(int &now, int val)
{
if (!siz)//空树
{
t[++siz].val = val;
return;
}
if (!now)//该节点为空,新加节点
{
now = ++siz;
t[now].val = val;
}
else
if (val <= t[now].val)//now 权值大于 val
ins(t[now].ls, val);
else
if (val > t[now].val)//now 权值小于 val
ins(t[now].rs, val);
}
删除
删除 当前节点右子树最小值
int del_min(int &now)//删除 now 节点右子树的最小值
{
if (!t[now].ls)
{
int tmp = t[now].val;
if (now != root)
now = t[now].rs;
siz--;
return tmp;
}
else
return del_min(t[now].ls);
}
删除 val
删除分为三种情况:
-
若 \(now\) 为叶子节点,直接删除该节点
-
若 \(now\) 为链节点,即只有一个儿子的节点,返回这个儿子。
-
若 \(now\) 有两个非空子节点,一般是用它左子树的最大值或右子树的最小值代替它,然后将它删除。
void del(int &now, int val)// now 有可能会被修改
{
if (t[now].val == val)
{
if (t[now].ls && t[now].rs)//如果当前节点存在左右子树,即不是链节点
t[now].val = del_min(t[now].rs);//删除当前节点右子树的最小值
else
now = t[now].ls + t[now].rs;//如果当前节点是叶子节点,那么直接删除
siz--;
return;
}
if (t[now].val > val)
del(t[now].ls, val);
else
del(t[now].rs, val);
}
判断 val 是否存在于某集合中
直接暴力递推判断即可。
bool check(int now, int val)
{
if (!now)//空树
return false;
if (val == t[now].val)
return true;
if (val < t[now].val)
return check(t[now].ls, val);
return check(t[now].rs, val);
}
查询最大值 / 最小值
最大值
int find_max(int now)
{
if (!t[now].rs)
return t[now].val;
return find_max(t[now].rs);
}
最小值
int find_min(int now)
{
if (!t[now].ls)
return t[now].val;
return find_min(t[now].ls);
}
遍历二叉搜索树
菜鸡只会暴力
void tvs(int now) //遍历二叉树
{
if (t[now].ls)
tvs(t[now].ls);
printf("%d ", t[now].val);
if (t[now].rs)
tvs(t[now].rs);
}
写在后面
-
关于代码实现实用性:
其实暴力维护二叉搜索树效率有时候并不算很高,如果是一棵树链那么程序的复杂度会达到 \(O(n^2)\) 。
通常情况下可以利用 \(Splay\) , \(Treap\) 进行二叉搜索树的实现与维护。
-
关于博客笔记实用性:
如果觉得我讲的不好,请阅读这篇博客。
-
关于娱乐语言实用性:
就是瞎
JB扯淡。。时间紧凑者自行忽略。-
啊啊啊我太菜了 -
上午有个博园的小妹妹来机房,手法娴熟的开电脑,一顿操作猛如虎,然后一屁股坐到了我斜对面,抬头能瞟见的那种。。。然后NJ问她学到哪了,她说我和他们进度不一样,我学得慢,我才学到二叉树。。。。
\[\huge{!\ !\ ?\ ?\ 屮 \ ?\ ?\ !\ !} \]
-
-
鸣谢:
Luckyblock 笔记 (老哥我想你了。。。/kk)