数据结构基础--搜索树

一、二叉搜索树

二叉搜索树的定义

二叉搜索树也被称为二叉排序树、二叉查找树 

二叉搜索树:

(1)二叉树任意两个节点的关键字值不相同

(2)若左子树不为空,则左子树上所有结点的关键字值均小于根结点的关键字值

(3)若右子树不为空,则右子树上所有节点的关键字值均大于根节点的关键字值

(4)左,右子树也分别是二叉搜索树

如果以中序遍历一棵二叉搜索树,将得到一个以关键字值递增排列的有序序列

二叉搜索树的搜索

 算法思想:

(1)若二叉搜索树为空,则查找失败,返回NULL

(2)若二叉搜索树不为空,则将给定值x与根节点关键字比较:①如果x=data,则查找成功。返回根节点地址②如果x>data,则接下来查找右子树③如果x<data,则接下来查找左子树

所以需要递归实现

#include<stdio.h>
#include<stdlib.h>

typedef int BTDataType;
typedef struct BinaryTreeNode
{
	BTDataType data;
	struct BinaryTreeNode* left;
	struct BinaryTreeNode* right;
}BTNode;

BTNode* BTSearch(BTNode* bt, BTDataType x)
{
	if (bt == NULL)
	{
		return NULL;
	}
	if (bt->data==x)
	{
		return bt;
	}
	else if (x<bt->data)
	{
		return BTSearch(bt->left, x);
	}
	else
	{
		return BTSearch(bt->right, x);
	}
}

 迭代实现;

//迭代实现
BTNode* BTSearch(BTNode* bt, BTDataType x)
{
	while (bt)
	{
		if (x<bt->data)
		{
			bt = bt->left;
		}
		else if (x>bt->data)
		{
			bt = bt -> right;
		}
		else
		{
			return bt;
		}
	}
	return NULL;
}

查找次数取决于被查找节点的深度,也就是节点所在层数==查找次数

最好情况查找时间复杂度:O(log2N)

最坏情况查找时间复杂度;O(N)

平均查找时间复杂度取决于树的形态

二叉搜索树的插入

如果二叉树为空,则插入节点作为根节点插入到空树中

否则,继续在左右子树上查找

树中已经有,则不再插入

树中没有,则将元素查插入到搜索结束处

不同插入次序的序列生成不同形态的二叉排序树

bool BTInsert(BTNode* bt,BTDataType x)
{
	BTNode* p = bt, * q, * newnode;
	while (p)
	{
		q = p;
		if (x<p->data)
		{
			p = p->left;
		}
		else if (x>p->data)
		{
			p = p->right;
		}
		else
		{
			printf("insert fail\n");
			return false;
		}
	}
	newnode = (BTNode*)malloc(sizeof(BTNode));
	newnode->data = x;
	newnode->left = NULL;
	newnode->right = NULL;
	if (bt==NULL)
	{
		bt = newnode;
	}
	else if (x<q->data)
	{
		q->left = newnode;
	}
	else
	{
		q->right = newnode;
	}
	return true;
 }

二叉搜索树的删除

需要保证删除节点之后,二叉搜索树仍然还是二叉搜索树①将因删除节点而断开的二叉链表重新链接起来②防止重新链接后树的高度增加


这种删除操作的具体实现步骤可描述如下.
首先搜索待删除元素所在的结点,记为结占n并记录n的双亲结点(。如果不存在待删除的元素,应返回NotPresent。如果存在待删除的元素,则删除结点p的操作可分下面两种情况讨论。 
(1)若结点p有两棵非空子树,这时需搜索p的中序遍历次序下的直接后继(或直接前驱)结点,设为结点s。然后将s的值复制到待删除结点p中,称为替代。因为结点s最多只有一棵非空子树,这样一来,问题便转化为“被删除的结点最多只有一棵非空子树”的问题。
(2)当结点p只有一棵非空子树或p是叶结点时,若p->LChild非空,则由p->LChild取代p,否则由p->RChild取代p。事实上,若p是叶结点,将以NULL取代p. 
程序中用以取代p 的结点由指针c指示,c可以为空指针。若被删除的结点原来是根结点,则删除后,用来取代它的结点(可以是空树)成为新的根结点。一个被删除的结点,如果原来是其双亲的左孩子,则取代它的结点(子树)也应成为该双亲的左孩子(左子树),反之亦然。最后需要使用free语句释放结点p所占用的空间。 

//二叉搜索树的插入
bool BTDelete(BTNode* bt,BTDataType x)
{
	BTNode* c, * r, * s, * p = bt, * q;
	while (p&&p->data!=x)//从根节点开始查找data=x的节点p
	{
		q = p;//q为p的双亲结点
		if (x<p->data)
		{
			p = p->left;
		}
		else
		{
			p == p->right;
		}
	}
	if (p==NULL)//找不到被删除的节点就返回空
	{
		printf("Not Present\n");
		return false;
	}

	if (p->left&&p->right)//p的两个子树为空
	{
		s = p->right;//为p的右孩子
		r = p;
		while (s->left)//搜索p的中序直接后继结点
		{
			r = s;
			s = s->left;
		}
		p->data = s->data;//令p指示被删除的结点,q为p的双亲
		p = s;
		q = r;
	}
	if (p->left)//c指示取代p的子树
	{
		c = p->left;
	}
	else
	{
		c = p->right;
	}
	if (p==bt)//如果被删除的是根结点,则用c为新的根
	{
		bt = c;
	}
	else if (p==q->left)//否则c取代p
	{
		q->left = c;
	}
	else
	{
		q->right = c;
	}
	free(p);
	return true;
}

二、平衡二叉树

含有n个节点的二叉搜索树的平均查找长度和树的形态有关

平衡二叉树的定义

平衡二叉树(balanced binary tree)是带有平衡条件的二叉搜索树,又称为AVL树。平衡二叉树首先是二叉搜索树,其次满足以下条件:

(1)其根的左子树、右子树高度之差的绝对值不超过1

(2)其根的左子树与右子树也是二叉平衡树

给每个结点附加一个数字,为该结点左子树与右子树的高度差,这个数字被称为该结点的平衡因子。

平衡因子=结点左子树高度-结点右子树高度

平衡二叉树的平衡调整方法

将新元素在最小子树s上插入的类型分为以下四种:

LL型:新元素插入在s的左孩子的左子树上

LR型:新元素插入在s的左孩子的右子树上

RL型:新元素插入在s的右孩子的左子树上

RR型:新元素插入在s的右孩子的右子树上

调整原则:①降低高度②保持二叉搜索树的性质

首先计算插入后各个结点的平衡因子树,判断是哪一种类型,将数字大小进行排序,取中间值为根节点

对于相同的平衡因子数,选取最小的子树进行平衡

平衡二叉树平衡调整代码实现: 

#include<stdio.h>
#include<stdbool.h>
#include<stdlib.h>
struct TreeNode
{
	int val;
	struct TreeNode* left;
	struct TreeNode* right;
};


int maxDepth(struct TreeNode* root)
{
	if (root == NULL)
	{
		return 0;
	}
	int leftdeep = maxDepth(root->left);
	int rightdeep = maxDepth(root->right);
	return leftdeep > rightdeep ? leftdeep + 1 : rightdeep + 1;
}
bool isBalanced(struct TreeNode* root)
{
	if (root == NULL)
	{
		return true;
	}
	int leftdeep = maxDepth(root->left);
	int rightdeep = maxDepth(root->right);
	return abs(leftdeep - rightdeep) < 2 && isBalanced(root->left) && isBalanced(root->right);
}

平衡二叉树搜索树的缺陷

平衡二叉树搜索树的高度是logN,这个查找次数在内存中是很快的。但是当数据都在磁盘中时, 访问磁盘速度很慢,在数据量很大时,logN次的磁盘访问,是一个难以接受的结果。 

三、m叉搜索树

m叉搜索树的定义

一颗m叉搜索树或者为空,或者满足如下性质:

(1)根节点最多有m课子树,并具有:n,P0,(K1,P1),(K2,P2),.....,(Kn,Pn)

其中Pi是指向子树的指针,0<=i<=n<m,Ki是元素的关键字值,1<=i<=n<m

(2)Ki<Ki+1,1<=i<n

(3)子树Pi上的所有关键字值都小于Ki+1,大于Ki,0<i<n

(4)子树Pn上所有的关键字值都大于Kn,子树P0上的所有关键字值都小于K1

(5)子树Pi也是m叉搜索树,0<=i<=n

 图7.17是多叉搜索树结点的结构,图7.18给出了一棵四叉搜索树,图中的小方块代表空树。空树也称为失败结点,因为这是当搜索的关键字值k不在树中时到达的子树。失败结点不包含元素。图7.18中,根结点有两个孩子,树的结点中的关键字值有序排列,孩子数最多为4,所以称为四叉搜索树。

m叉搜索树的高度

 高度为h的m叉搜索树中最多有(m^h)-1个元素

含有N个元素的m叉搜索树的高度在logm(N+1)到N之间,m为底数

四、B-树

B-树的定义

平衡搜索树基础上找优化空间:①压缩高度采用多叉树替代二叉树②一个结点里面有多个关键字及映射的值,在一个结点中存放多个元素而不是一个元素。

一棵m阶B-树是一棵m叉搜索树,它或者为空,或者满足如下性质:

(1)根结点至少有两个孩子

(2)每个分支结点都包含k-1个关键字和k个孩子,ceil(m/2)<=k<=m,ceil为向上取整函数

(3)每个叶子结点都包含k-1个关键字,其中ceil(m/2)<=k<=m

(4)所有的叶子结点都在同一层

(5)每个结点中给的关键字从小到大排列,结点当中k-1个元素正好是k个孩子包含的元素的值域划分

(6)每个结点的结构为:(n,A0,K1,A1,K2,A2,… ,Kn,An)其中,Ki(1≤i≤n)为关键字,且Ki<Ki+1(1<+i<=n-1)。Ai(0≤i≤n)为指向子树根结点的指针。且Ai所指子树所有结点中的关键字均小于Ki+1。n为结点中关键字的个数,满足ceil(m/2)-1≤n≤m-1。

一般情况下m会设置的比较大, 

B-树的高度

 一颗B-树所包含的元素总数是该B-树的空结点的总数减1。设B-树中空树的总是为s,那么一颗B-树的元素总数N=s-1

含有N个元素的m阶B-树的高度

 B-树的搜索

B-树的搜索与二叉搜索树的搜索方式类似,B-树上进行搜索的过程是一个顺指针查找结点和在结点关键字中交叉进行的过程。

 B-树的插入

①首先进行搜索操作,查找是否存在关键字值相同的元素。如果存在,则插入失败;如果不存在,搜索会停留在空树位置②执行插入操作,插入结点中。插入后需要判断关键字值数量与孩子结点数量,如果关键字值数量=m,则进行分裂操作。

 从上面的图可以得到在m阶B-树中插入新元素的方法。
(1)搜索。在B-树中搜索给定关键字值的元素。如果搜索成功,表示有重复元素,则插入运算失败终止;否则转步骤(2)将新元素插入搜索失败结点上一层处的叶结点(设地址为q)。
(2)插入。将新元素和一个空指针插入结点q。如果插入后,结点q未上溢出,即结点包含的元素个数未超过m-1(指针数未超过m),则插入运算成功终止。否则转步骤(3)进行结点的分裂操作。
(3)分裂。以「m/27处的元素为分割点,将结点q一分为三:第1个位置至第m/2-1个位置的元素保留在原来的结点q中;第[m/2]+1个位置至第m个位置的元素存放在新创建的结点(设地址为q')中;而第「m/27个位置的分割点元素和新结点地址q'插入结点q的双亲结点。继续检查此双亲结点的上溢出问题。如果没有上溢出,则插入运算结束,否则重复执行步骤(3),继续该双亲结点的分裂操作,直至不再产生上溢出现象。
需要注意的是,如果按照(3)的原则,根结点产生分裂,由于根结点没有双亲,那么分裂产生的两个结点的指针以及分割点元素将组成一个新的根结点,从而导致B-树长高一层。

关于分裂:如果关键字的数量=m,则满,满则分裂出兄弟。同时将一半的关键字值与孩子结点分裂给兄弟。如果满了的结点有m个关键字,则分裂右边的m/2给兄弟,并将中位数提取给父亲结点。如果没有父亲结点就创建新的根。

B-树的删除

 ①首先进行搜索操作,查找元素是否存在②如果被删除的元素在叶结点上,则直接从该叶结点中删除该元素,并检查是否发生下溢出。如果没有下溢出,则删除运算结束,否则处理下溢出。③如果被删除的元素不在叶结点上,则首先执行替代操作,用该元素右子树上的最小元素取代它

如果该结点为最后一层的非终端结点,有下列 3 种可能,下面用3阶B-树举例: 

  • 被删关键字所在结点中的关键字数目不小于⌈m/2⌉,则只需从该结点删除该关键字 Ki 以及相应的指针 Ai 。

  • 被删关键字所在结点中的关键字数目等于⌈m/2⌉-1,而与该结点相邻的右兄弟结点(或者左兄弟)结点中的关键字数目大于⌈m/2⌉-1,只需将该兄弟结点中的最小(或者最大)的关键字上移到双亲结点中,然后将双亲结点中小于(或者大于)且紧靠该上移关键字的关键字移动到被删关键字所在的结点中。

  • 被删除关键字所在的结点如果和其相邻的兄弟结点中的关键字数目都正好等于⌈m/2⌉-1,假设其有右兄弟结点,且其右兄弟结点是由双亲结点中的指针 Ai 所指,则需要在删除该关键字的同时,将剩余的关键字和指针连同双亲结点中的 Ki 一起合并到右兄弟结点中。

 从上面的例子可以得到从B-树删除元素的步骤。
(1)搜索。在B-树中搜索给定关键字值的元素。如果搜索不成功,则删除运算失败终止;否则转(2)执行元素的删除操作。
(2)删除。首先判断删除类型,根据删除类型选用相应的删除方法。
如果属于情形1,即被删除的元素在叶结点中,则转(3)执行从叶结点中删除该元素的操作。如果属于情形2,即被删除的元素不在叶结点中,则用该元素右子树上的最小元素(必定在叶结点中)取代它,从而将问题转化为情形1,然后转(3)执行从叶结点中删除该替代元素的操作。
(3)从叶结点中删除元素。首先从叶结点中直接删除该元素,如果删除元素后没有下溢出(即当前结点包含m/2-1个元素),则删除运算成功终止。否则首先考虑采用“借”的方法处理下溢出(请注意:为避免删除结果不唯一,这里我们约定“借”操作遵循先左后右的顺序,即若左侧兄弟有富余,则从左侧兄弟“借”一个元素,否则,若右侧兄弟有富余,则向右侧兄弟“借”一个元素)。如果左右两侧兄弟结点都没有富余,则采用“并”的方法处理下溢出(同样,我们约定“并”操作遵循先左后右的顺序,即若当前结点有左侧兄弟,则将该结点与其左侧兄弟“并”成一个结点,否则与右侧兄弟“并”成一个结点),然后继续检查其双亲结点的下溢出问题。如果没有下溢出,则删除运算结束,否则继续该双亲结点的先“借”后“并”操作,直至不再有下溢出现象产生。
同样需要注意的是,如果“并”操作导致根结点中的一个元素被删除,并且该结点只包含一个元素,则根结点成为不包含任何元素的空结点,此时B-树将变矮一层。

  • 2
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值