算法导论 第14章 数据结构的扩张(一)动态顺序统计树

动态顺序统计树(Orderly Statistic Tree)

       在一个包含n个元素的集合中,第i个顺组统计量就是该集合的第i小关键字,这里的i我们称为秩。在一个无序的集合中,确定任意一个统计量都能在O(n)的时间内找到。现在,我们介绍一种更快速的方法,利用对红黑树,作出一些修改和扩张,使得任意统计量的查找耗时降为O(lgn),这样的一棵树称之为顺序统计树。

       顺序统计树的节点中增加了一个域size,它记录了在以本节点为根的子树中节点的数量(包括该节点本身),定义哨兵nil的size域为0 。对于节点x,它的size域的计算如下:

                    size[x] = size[left[x]] + size[right[x]] + 1;

下图是一棵顺序统计树的例子

       1、检索确定秩的元素

        给定一个确定的秩i,过程select查找出秩为i,即该树中第i小的节点,返回指向该节点的指针,算法如下:

select(x, i)
{
	r < -left[x] + 1;
	if (i == r) return x;
	else if (i < r)
		return select(left[x], i);
	else return select(right[x], i - r);
}

        算法select中,第三行先计算当前节点x的秩r,然后与给定的秩i进行比较,如果相等,那么说明x即为要找的目标;如果i < r,则继续在left[x]中递归寻找;若i > r,则需要在right[x]上查找,此时需要查找的节点的在右子树上的秩为i-r,即是右子树上第(i - r)小的元素。

        结合上图,我们执行以下这个过程select(T,17)。第一趟,计算出关键字为26的秩为13,比17小,那么所找节点肯定在右子树,且在此子树的秩为4;第二趟,计算出根的右子树中关键字41的秩为6,大于4,那么所找节点必定在其左子树;第三趟,计算出关键字41的左孩子30的秩为2,小于4,那么所找节点必定在该关键字节点的右子树中的第(4 - 2)位置上;第四趟,计算出关键字为38的节点的秩为2,两者相等,说明已找到,返回该节点指针,查找结束。

       select过程和普通的查找locate一样,每进行一次,就下降一层,最终要么找到,要么指向nil,结束。所以时间复杂度为O(lgn),与树高成正比。


       2、给定一个元素,确定它的秩

        给定一个指向树中某节点的指针,确定该节点在树中的秩,先给出算法

rank(x)
{
	r <- size[left[x]] + 1;
	while (parent[x] != nil)
	{
		if (x == right[parent[x]])
			r <- size[left[parent[x]]] + 1 + r;
		x <- parent[x];
	}
	return r;
}

         该算法的执行是从局部到整体,从子树到整棵树推进的:一开始时,在当前以该节点,也就是x为根的子树中计算出x的秩,然后如果该节点不是整棵树的树根且其为其父节点的右孩子,那么在以其父节点为根的子树中,x的秩还要加上其兄弟子树的size,外加父节点本身,如下图关键字47的秩;如果其为其父节点的左孩子,那么在以其父节点为根的子树中,x的秩没变,那么只需修改指针,继续向上一层推进,直至到根。while循环中每执行一次,就向上推进一层,因此时间为O(lgn)。


        3、对子树规模的维护

        在每个节点增加了size域后能够快速确定顺序统计信息,那么,在插入和删除过程中如何对这个size域进行维护呢?毕竟,如果不能得到有效的维护,是不可能达到所期望的目标的。改变树的结构的操作只有插入、删除以及旋转,接下来我们就着手讨论这三个方面如何对size进行维护。


         插入

         我们知道,对于红黑树的插入,分为两个阶段。第一个阶段是自顶向下搜寻找到插入位置,然后在此处插入节点;第二阶段是从该节点开始自底向上调整该路径相关节点以保持红黑性质,其中会涉及到旋转。

         那么在第一阶段的搜寻过程中,对每个被遍历过的节点,其size域都增加1,因为这个新插入节点最终会是它们的子孙。这个过程时间复杂度为O(lgn)。

         在第二阶段调整中,改变红黑树的仅仅只会是旋转,且最多旋转两次,另外旋转是一种局部操作,只涉及两个节点,时间为O(lgn),对于左旋,我们只需要在代码中增加下述两行即可:

size[y] <- size[x];
size[x] <- size[left[x]] + size[right[x]] + 1;

下图显示了旋转过程中size域是如何改变的,右旋与此对称。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值