介绍
有关顺序统计量在前文中位数与顺序统计量里有详细介绍,在前文是用数组实现的。而在这里,是基于红黑树的实现,效率更加高效。
实现
在红黑树的基础上,增加了一个size数据域,用来标明以当前结点为子树的结点总数,也包含自身:
typedef struct _Node
{
char color;
int data;
int size;
_Node* parent;
_Node* left;
_Node* right;
}Node;
然后,就是创建结点的时候,默认结点数为1:
void initNode(Node* node)
{
node->parent = g_sentry;
node->left = g_sentry;
node->right = g_sentry;
node->size = 1;
}
void init()
{
g_sentry = new Node;
g_sentry->color = static_cast<char>(BLACK);
initNode(g_sentry);
g_sentry->size = 0;
g_tree = new Tree;
g_tree->root = g_sentry;
}
并且,默认哨兵的结点数为0,即不统计哨兵。
接着就是旋转,因为旋转交换了2个节点的位置,固需要修改它的结点值:
左转:
void leftRotate(Tree* tree, Node* node)
{
Node* y = node->right;
node->right = y->left;
y->left->parent = node;
y->parent = node->parent;
if (g_sentry == node->parent)
{
tree->root = y;
}else if (node == node->parent->left)
{
node->parent->left = y;
}else
node->parent->right = y;
y->left = node;
node->parent = y;
y->size = node->size;
node->size = node->left->size + node->right->size + 1;
}
把旋转之前的父结点的包含结点数给右结点,然后重新统计父结点的包含结点数,右转一样:
void rightRotate(Tree* tree, Node* node)
{
Node* x = node->left;
node->left = x->right;
x->right->parent = node;
x->parent = node->parent;
if (g_sentry == node->parent)
{
tree->root = x;
}else if (node == node->parent->left)
{
node->parent->left = x;
}else
node->parent->right = x;
x->right = node;
node->parent = x;
x->size = node->size;
node->size = node->left->size + node->right->size + 1;
}
插入的时候,因为多了一个结点,固在这个结点的路上的所有结点都需要+1结点数:
void treeInsert(Tree* tree, Node* node)
{
assert(g_sentry != node);
Node* y = g_sentry;
Node* x = tree->root;
while (g_sentry != x)
{
y = x;
if (node->data < x->data)
{
x = x->left;
}else
x = x->right;
}
node->parent = y;
if (g_sentry == y)
{
tree->root = node;
}else if (node->data < y->data)
{
y->left = node;
}else
y->right = node;
node->left = g_sentry;
node->right = g_sentry;
node->color = static_cast<char>(RED);
Node* p = node->parent;
while(p != g_sentry)
{
++ p->size;
p = p->parent;
}
RBInsertFixup(tree, node);
}
而删除和插入相反,再删除以后,需要把这条路径上所有的值都-1:
Node* treeDelete(Tree* tree, Node* node)
{
assert(g_sentry != node);
Node* p = node->parent;
while(p != g_sentry)
{
-- p->size;
p = p->parent;
}
Node* y = g_sentry;
if (g_sentry == node->left
|| g_sentry == node->right)
{
y = node;
}else
y = treeSuccessor(node);
Node* x = g_sentry;
if (g_sentry != y->left)
{
x = y->left;
}else
x = y->right;
x->parent = y->parent;
if (g_sentry == y->parent)
{
tree->root = x;
}else if (y == y->parent->left)
{
y->parent->left = x;
}else
y->parent->right = x;
if (y != node)
{
node->data = y->data;
}
if (y->color == static_cast<char>(BLACK))
{
RBDeleteFixup(tree, x);
}
return y;
}
然后在遍历输了的时候增加输出结点数:
void inorderTreeWalk(Node* node)
{
if (g_sentry != node)
{
inorderTreeWalk(node->left);
cout << node->data << "--" << node->size << " ";
inorderTreeWalk(node->right);
}
}
然后就是求出第i大的顺序统计数的函数了:
Node* select(Node* node, int order)
{
int r = node->left->size + 1;
if (order == r)
{
return node;
}else if (order < r)
{
return select(node->left, order);
}else
{
return select(node->right, order - r);
}
}
根据包含的结点数来判断当前的第i大的数据的位置,也可以根据相应的结点得出他位于序列的第几:
int rank(Tree* tree, Node* node)
{
int order = node->left->size + 1;
Node* y = node;
while (y != tree->root)
{
if (y == y->parent->right)
{
order += (y->parent->left->size + 1);
}
y = y->parent;
}
return order;
}
测试代码如下:
int _tmain(int argc, _TCHAR* argv[])
{
init();
for (int i = 0; i < 10; ++ i)
{
Node* p = createNode(i);
treeInsert(g_tree, p);
}
Node* q = createNode(15);
treeInsert(g_tree, q);
for (int i = 20; i < 100; ++ i)
{
Node* p = createNode(i);
treeInsert(g_tree, p);
}
inorderTreeWalk(g_tree->root);
cout << endl;
cout << "red black tree hight = " << getHight()<< endl;
Node* pFind = treeSearch(g_tree->root, 15);
assert(g_sentry != pFind);
cout << "find data = " << pFind->data << endl;
pFind = iterativeTreeSearch(g_tree->root, 15);
assert(g_sentry != pFind);
cout << "iterative find data = " << pFind->data << endl;
Node* predecessor = treePredecessor(pFind);
assert(g_sentry != predecessor);
cout << pFind->data << "'s predecessor is " << predecessor->data << endl;
Node* successor = treeSuccessor(pFind);
assert(g_sentry != successor);
cout << pFind->data << "'s successor is " << successor->data << endl;
Node* orderSelect = select(g_tree->root, 11);
assert(orderSelect != g_sentry);
cout << "order 11 is " << orderSelect->data << endl;
int order = ::rank(g_tree, orderSelect);
cout << "orderSelect is at " << order << endl;
pFind = treeDelete(g_tree, pFind);
assert(pFind == q);
cout << "delete pFind success" << endl;
inorderTreeWalk(g_tree->root);
cout << endl;
cout << "red black tree hight = " << getHight()<< endl;
pFind = treeSearch(g_tree->root, 15);
assert(g_sentry == pFind);
cout << "not find 15" << endl;
orderSelect = select(g_tree->root, 11);
assert(orderSelect != g_sentry);
cout << "order 11 is " << orderSelect->data << endl;
finit();
return 0;
}
输出结果如下:
![orderstatistics-result](https://i-blog.csdnimg.cn/blog_migrate/4d9b99650824a1453540ce169f155392.png)
可以去计算一下,输出的结果值是否正确。
总结
总体来说,复杂度和红黑树相当。在效率上对于红黑树并没有多大的影响,但是确可以方便的输出当前第i个顺序统计量,并且也可以根据结点才得出它的所包含的结点数,对于一些特定的场合,比如说,需要知道第i大的值的地方是比较有用的。