在之前的随机选择算法中,我们可以很快的在集合中寻找到第i小的元素,然而,这样的集合并不支持动态的扩充。这一节里,将介绍通过红黑树(具体可参考红黑树1,红黑树2两篇文章)的扩充,使得任意的顺序统计量都可以在短时间内查找到,而这样的数据结构同时也支持数据的更新。
这样的数据结构称为顺序统计树,如下图。树的节点大致上与红黑树的类似,但增加了一个记录子树大小的域size[x],定义哨兵Nil的子树大小为0,即size[Nil[T]]=0。有等式size[x]=size[left[x]]+size[right[x]]+1
顺序统计树主要有两种算法:
1.检索具有给定排序的元素
其实就是x的顺序就保存在他的左孩子的节点上,所以每次和左孩子记录的值进行比较,匹配成功,则直接返回;比左孩子小了,那么在做孩子处递归调用算法;否则在右孩子处递归调用算法。
2.确定一个元素的秩
和上一个差不多,只是传入的节点,返回的节点所处的位置。
所有节点在插入的时候时需要注意,因为插入的过程就是和某一个节点比较大小,如果小于这个节点,那么就往左孩子处递归调用算法,否则的话向右孩子处递归。每次递归的时候,都需要给当前节点的size位加上1。一直递归到底层,这个时候,由于红黑树的插入性质,需要做左旋或者右旋的一些操作(颜色的改变不会影响到子树的大小),旋转之后需要重新确定好节点的大小。
下面给出代码(基于之前的红黑树的,所以嘛有点长==):
PS:其实改动的代码不到100行,其他的就是红黑树的源代码
#include <stdio.h>
#include <stdlib.h>
typedef enum Color{Red,Black}Color;
/*这里定义树的结构,每个节点为Node结构体,再加上一个头指针Tree*
在红黑树中,多了一个颜色的域Color,这里用枚举表示
顺序统计数中,多了一个表示子树大小的Size*/
typedef struct Node
{
int data;
Color color;
struct Node* left;
struct Node* right;
struct Node* parent;
int Size;
}Node;
Node Nil={0,Black,NULL,NULL,NULL};
typedef struct Tree
{
Node* Root;
}Tree;
/*一下的操作主要针对普通的二叉搜索树进行,但一些搜索、前去后继等操作也是可以直接用的*/
/*对树进行中序遍历*/
void Mid_Traverse(Node* Root)
{
if(Root!=&Nil)
{
Mid_Traverse(Root->left);
printf("%d ",Root->data);
Mid_Traverse(Root->right);
}
}
/*普通二叉树的插入操作,对红黑树不能用这个函数!*/
/*以下函数是对树进行插入操作
定义两个Node变量x和y,一开始x指向根节点,y为空
然后将x的值一次往下递减向左边下降还是右边依据和z的比较,而y的值一直都是x的父节点,以防当x为空时,就找不到这棵树了
然后让z的父节点指向y,相当于把z放到x的地方
当然,需要判断这棵树是否一开始就是空的,如果y是空的话,那么直接把更节点给z
否则的话更具z的值与y比较大小,判断是把z放到左边还是右边*/
void Tree_Insert(Tree* T,Node* z)
{
Node* y=NULL;
Node* x=T->Root;
while(x!=NULL)
{
y=x;
if(z->data<x->data)