ASL和二叉排序树

(1)概念

假设有一个线性表或者链表,里面包含有100个数据,你要在这100个数据里找到你要的A,那这就称为“一次查找”,在这一次查找中,需要比较多少次才能找到你要的A,这就称为查找长度。

在这100个数据中,100个数据的查找会有100个不同的查找长度

例如A是第一位的,查找长度为1,B是第三位,查找长度为3,C是第十位,查找长度为10…

把这100个不同的数据的不同查找长度加起来,除以数据的总个100,得到的就是平均查找长度

简单理解其实就是我平均查一个数据大约要比较多少次

查找成功的平均查找长度(ASL成功):指查找的数据存在,成功查找到对应的数据的平均查找长度

查找失败的平均查找长度(ASL失败):指查找的数据不存在,查找失败的平均查找长度

在这里插入图片描述
Pi是查找到第i个数据的概率,一般假设每个数据的查找概率都是1/n;有的题目会专门给出每个数据不同的概率,这里Pi就要换
Ci是指找到第i个数据需要进行比较的次数,也就是利用题目给定的查找算法,要比较多少次才能找到当前数据。

ASL是衡量查找算法效率的最重要的指标。

(2)例子

1:假设有n个数据的数组或者链表,对其采用顺序查找的,求:

ASL成功:
若采用顺序查找方式(即从头到尾一个个比较),明显看出:
查找第1个数据长度1,查找第2个数据长度2,查找第3个数据长度3,查找第4个数据长度4…查找第n个数据长度n
根据等差数列求和公式得到:n*(1+n)/2
最后因为有n个数据,所以再除以n:n*(1+n)/2 / n
求得ASL成功:(1+n)/2

ASL失败:
假如题目给出说数组有哨兵位,即从后往前倒序查找,还要比较a[0]位的哨兵,则一共需要比较n+1次
假如题目没有说有哨兵位,则仅比较n次即可确定数据不存在

2:设给定一个无序数组:【29,13,37,7,10,16,19,32,33,41,43】,针对该数组生成一棵二叉排序树,并针对该二叉排序树进行折半查找,求ASL成功和ASL失败

生成二叉排序树(左小右大)思路如下:
循环无序数组
(1)如果当前树为空,则把当前元素做根结点
(2)如果树不为空,且当前元素比当前结点的元素小,则往左边递归找对应的位置
(3)如果树不为空,且当前元素比当前结点的元素大,则往右边递归找对应的位置
(4)直到某个结点的左孩子或者右孩子为空,那么就把当前元素按照左小右大的规则放在对应位置
伪代码如下:

int a[MaxLenght];   // 假设的无序数组
	TNode rootNode = null; // 创建一个根结点
	for(int i=0; i<MaxLenght; i++){
		// 把数组一个个的元素不断插入到二叉排序树中
		Create_Tree(rootNode, a[i]);
	}
	
	void Create_Tree(TNode node, int value){
		if(node == null) {
			// 如果是空,则生成一个新的结点,该新结点一定是叶子结点
		 	node = (TNode)malloc(sizeof(TNode));
			// 二叉树中叶子结点左右孩子一定是null
			node.lchild = node.rchild = null;
			node.data = value;
			return 0;
		} else if (value < node.data) {
			// 待插入数据比当前结点的数据小,往左边找位置
			Create_Tree(node.lchild , value);
		} else if (value > node.data) {
			// 待插入数据比当前结点的数据大,往右边找位置
			Create_Tree(node.rchild , value);
		} else if (value == node.data) {
			// 如果有重复值,无需插入
			return 1;
		}
	}

生成后的二叉排序树:
在这里插入图片描述
对其进行折半查找,折半查找的前提就是一组数组单调有序才能使用,所以先通过中序遍历(左中右,刚好能准确转换出从小到大的顺序)得到有序数组:
下标:0,1 ,2 ,3 ,4 ,5 , 6 , 7 , 8 , 9 , 10
数据:7,10,13,16,19,29,32,33,37,41,43

对照着生成树的图,层数就是每个数据元素的比较次数,假设查找概率相同(每个都是1/n),共11个数据元素,则根据公式:
ASL成功 = (11 + 22 + 34 + 44 )/ 11 = 33/11 = 3
对照着生成树的图,如果查找元素比7还要小,则肯定查找失败,对应着结点7的左孩子位置为null,对比到结点7的左孩子位置,需要进行3次比较才能确认查找元素不存在(和29比,和13比,和7比,再下找没了,所以是3次比较),同一层中,空的还有16的左孩子结点(代表着大于13小于16的数据也是不存在,但是也要比较三次才能确认),32的左孩子结点(大于29小于32的数据),41的左孩子结点(大于37小于41的数据);所以比较3次就能确定不存在(也就是找不到!查找失败!)的数据有4种
同理,比较4次才能确定查找失败的有大于7小于10和大于10小于13;大于16小于19和大于19小于29…

上面一大堆分析,其实更简单的方式就是数有多少个空的左右结点即可!第n层空的结点,仅需要比较n-1次。

ASL失败 = (3*4 + 4 * 8)/ 12 = 11/3

3:对长度为3的顺序表进行顺序查找,若查找第一个元素的概率为1/2,第二个元素概率为1/3,第三个元素概率为1/6;则成功平均查找长度为:

注意非等概率查找,需要根据公式:Pi(概率)* Ci(比较次数),i从1到3
则有:(1/2)+ (2/3) + (3/6)=5/3

删除二叉排序树的结点操作:

在这里插入图片描述
针对所示二叉排序树,若要删除某个结点X:
(1)如果X是叶子结点,直接删除
(2)如果X是非叶子,且被X只有一个子树(左右皆可,但只能要么有左,要么有右),则直接用子树顶替X的位置
(3)如果X左右子树都有,那么用右子树中的中序遍历的第一个结点去填补X的位置
解析:为什么要用右子树的中序遍历的第一个结点去填补X?
因为要满足删除X之后,填补上去的仍能满足左小右大的原则,X的右孩子的中序遍历的第一个元素,一定是比左子树所有元素大,同时又是右子树中最小的一个元素,所以它放在X的位置,刚好满足左小右大原则。
在这里插入图片描述

查找时间复杂度:

二叉排序树的查找过程于二分查找类似,但是对于同一组数组插入顺序不一致会产生不同的二叉排序树,极端情况下,根结点a是所有数据中最小的,这个时候会造成其它所有结点都堆积在a的右侧,当插入的数据是有序的(即后一个元素一定比前一个元素小/大),会导致一边倒的二叉排序树,查找时间复杂度最坏就是O(n) ;
二分查找的一般我们都是直接针对给定的有序数组进行,所以它的生成树一定唯一,每一轮查找都能排除掉一半的数据,所以它的时间复杂度是:O(log2n)

【2013】在任意一棵非空二叉排序树T1中,删除结点V后形成二叉排序树T2,再将V插入T2形成二叉排序树T3。下列关于T1和T3的叙述中,正确的是:

如果V是T1的叶子结点,则T1和T3相同。
分析:通过上面删除的演示可以看出,删除叶子结点再加回原来的叶子结点,二叉排序树完全没发生变化
如果V不是T1的叶子结点,则T1和T3不同。
分析:如果不是叶子结点,那么删除后要拿右子树的中序遍历的第一个元素顶上,V再回来的话,肯定没办法回到原来的位置,新加入二叉排序树的数据元素一定一定是叶子结点。所以T1和T3不同,绝对不同。

注意⚠️:该类似的问题2019年也出过一道非常类似的。但是是平衡二叉树

【2019】在任意一棵非空平衡二叉树(AVL树中)T1中,删除某结点V后形成平衡二叉树T2,再将V插入T2形成平衡二叉树T3。则下列关于T1和T3的叙述,正确的是:

若V是T1的叶子结点,则T1和T3可能不相同
分析:
由于是平衡二叉树,删掉一个叶子结点可能会引起平衡旋转,所以会影响整棵树的形态。这个时候T1T3不相同
如果删掉叶子结点V没有引起平衡旋转,则T1=T3
所以它们有可能相同也有可能不相同

若V不是T1的叶子结点,则T1和T3有可能相同
V不是叶子结点,删除后肯定会引起树的变动,这个时候如果把V再插入,有可能引起平衡,使其又恢复成T1,例子如下:
在这里插入图片描述

一棵二叉排序树按先序遍历得到的序列为(50,38,30,45,40,48,70,60,75,80);试画出该二叉排序树,并求出等概率下查找成功和查找失败的平均查找长度

先序遍历序列恢复成二叉树,第一个50肯定是根结点,比50小的都丢到左子树,比50大的都丢到右子树;
左子树中,第一个被访问到的38肯定是左子树的根结点,所有比38小的丢到左边,比38大的丢到右边
右子树中,第一个被访问到的70肯定是右子树的根结点,所有比70小的丢左边,比70大的丢右边
循环往复,就能复原出来了,
题目又说是等概率,直接Pi*Ci求出:
在这里插入图片描述

按照序列(40,72,38,35,67,51,90,8,55,21)建立一棵二叉排序树,画出该树,并求出在等概率情况下,查找成功的平均查找长度

40做根结点,根据二叉排序树默认的左小右大的原则,画出该树,再根据公式求ASL成功
在这里插入图片描述

最佳二叉排序树:高度最小,且左右子树的结点数量相差不超过1,则是最佳二叉排序树。最佳就佳在于它查询效率很高,既有排序树的查找方便,同时又是一棵平衡二叉树,而且还是左右结点相差不超过1的平衡二叉树。
给定关键字集合{25,18,34,9,14,27,42,51,38}; 假定查找各关键字的概率相同,请画出最佳二叉排序树。

思路:首先要对关键字序列做一个排序,找到中间的那个元素作为根结点,然后再按照左小右大不断往树里面加元素;由于我们已经确定了根结点,所以左右子树发生不平衡的时候,仅在子树内部调整,不允许调整根结点;不然就会出现左右两边元素之差大于1,就不是最佳了
同时,在生成左右子树的过程中,也不允许调整根结点。
在这里插入图片描述

编写一个算法,判断给定的二叉树是否是二叉排序树 / 求出二叉排序树中最小和最大的关键字

思路:根据先序遍历,看看是否递增有序,如果发生逆序,则不是二叉排序树;求最小最大关键字,只需要先序遍历,第一个就是最小,最后一个就是最大(或者更加直接一点,直接while循环去找最左下角的元素,就是最小元素;while循环去找最右下角的元素,就是最大元素)

既满足大根堆要求又满足二叉排序树的要求的一棵二叉树一定是仅有一个左孩子的单边树,左孩子的值比根结点的值小

因为大根堆要求根结点的值大于左右孩子,二叉排序树是左小右大,那么就只能有左孩子,因为二叉排序树的右孩子一定比根大,所以不能存在。
其次,大根堆是一棵完全二叉树,当上一层没满(没有右孩子),不能产生下一层的子结点,所以该符合条件的单边二叉树它的高度只能是2

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值