前言
今天在学习红黑树的时候,看到了引导课拿二叉排序树举例,想到之前还没写过二叉排序树的代码,所以这次特地写了一下,这里把二叉排序树的性质和写代码时出现的问题做一个分享。
概念
介绍
在写之前,要先了解什么是二叉排序树
二叉查找树,它有一个根节点,且每个节点下最多有只能有两个子节点,左子节点的值小于其父节点,右子节点的值大于其父节点。
我们依次插入数据:43,98,2,4,0,5
下图就是插入完成后二叉查找树的样子
数据操作无非是增删改查
对于二叉排序树
增就是insert节点
删就是delete节点
查就是查询值x是否存在于树中
二叉排序树存在意义的思考
这时候有人可能会问了:“查找用数组不香吗?一个for循环搞定”。
这里我们来分析一波时间复杂度
for循环遍历数组,最坏情况是遍历到最后一个元素,时间复杂度为O(n)
而二叉查找树的查找方式类似于二分查找,只要查找树的高度,我们都知道,查找树高的时间复杂度为O(logn)
这时候可能又有人要问了:“既然时间复杂度都是O(logn),那我直接二分查找不就好了?还要建树那么麻烦。”
问的好,但是我们再想想,二分查找前,要先对数组进行排序,我们如果用排序速度较快的归并或者快排,时间复杂度为O(nlogn),再加上二分查找的时间复杂度O(logn)(这里我们还没谈在数组中插入数据的时间复杂度和可能需要扩容的麻烦) ,也就是说,在最恶劣的情况下,第一次的查询时间是二叉排序树的两倍还多。
这时候可能又有读者骂我避重就轻了,“你说排序要时间,那你建树不要时间吗?”
骂的好,建树花费的时间和空间复杂度我们先不谈,我们谈谈 增 这个操作。
对于排好序的数组,在其中插入一个数据,不用再重新排序,只要遍历一遍就好,时间复杂度为O(n);
但对于二叉排序树,插入一个新数据,因为插入前要遍历的长度最长就是树的高度,所以时间复杂度为O(logn),插入一个数据可能效果还不明显,但插入10个,10000个,1亿个呢?很明显,从长期来看,二叉排序树的时间花费是明显优于使用二分查找的。
说了这么多,干货还一点没讲,下面我们进入正题!
操作以及代码展示
插入元素
根据二叉排序树的特点,我们很容易就能想出插入数据的思路,如果插入节点小于对比节点,且对比节点的左枝为空,就插在对比节点的左枝上,如果不为空,就对对比节点的左枝进行相同的插入操作(递归);如果插入节点比对比节点大,操作是一样的,就是换个方向。
对过程稍微分析一下,我们就知道要用到递归,代码如下
void BinSearchTree::insert(int x)
{
/*
这个重载是为了插入方便
只要insert一个值就可以了
不用自己再新建节点
*/
Node *root=new Node(x);
if(this->Root==NULL)
{
this->Root=root;
return;
}
Node *p=this->Root;
insert(p,root);
}
void BinSearchTree::insert(Node *p,Node *root)
{
/*
可不可以有相同的元素要根据需求来
如果不要相同元素,最好在用户插入的时候说明
如果有相同,插左边还是右边都可以,但定下左右就不能改了
这里我写的时候就简单一点,遇到相同的就不插了
*/
//学了点算法,感觉写起来没以前难了(滑稽)
if(root->data==p->data) return;//遇到相同,直接不插入
if(root->data < p->data