您的位置:首页 > 理论基础 > 数据结构算法

算法导论 14.1-7 顺序统计树求逆序对 O(nlgn)

2016-06-13 00:00 549 查看

一、题目

说明如何在O(nlgn)的时间内,利用顺序统计树对大小为n的数组中的逆序对(见思考题2-4)进行计数。

二、思考

求逆序数中介绍了使用树状数组或归并排序求逆序对,这里使用顺序统计数。

数组中某个数字s[i]的逆序数是指出现在s[i]之前,但是比s[i]大的数字的个数。

根据顺序统计量的Os_Rank(),每插入到一个元素x后,可以求得在已经出现的元素中,比x大的数字的个数

三、代码

#include <iostream>
using namespace std;

#define BLACK 0
#define RED 1

//顺序统计量树结点结构
struct node
{
int key;
bool color;
node *p;
node *left;
node *right;
int size;//以结点x为根的子树的内部结点的个数,x->key=x->left->key+x->right->key+1
node(node *init, int k):left(init),right(init),p(init),key(k),color(BLACK),size(1){}
};
//顺序统计量树结构
struct Os_Tree
{
node *root;
node *nil;
Os_Tree()
{
nil = new node(NULL, -1);//哨兵结点
root = nil;nil->size = 0;//初始时,树为空,root指向哨兵
};
};
//计算树T中进行顺序遍历后得到的线性序中x的位置
int Os_Rank(Os_Tree *T, node *x)
{
//置r为以x为根的子树中key[x]的秩
int r = x->left->size + 1;
node *y = x;
while(y != T->root)
{
//若y是p[y]的右孩子,p[y]和p[y]左子树中所有结点前于x
if(y == y->p->right)
r = r + y->p->left->size + 1;
y = y->p;
}
return r;
}
//左旋,令y = x->right, 左旋是以x和y之间的链为支轴进行旋转
//涉及到的结点包括:x,y,y->left,令node={p,l,r},具体变化如下:
//x={x->p,x->left,y}变为{y,x->left,y->left}
//y={x,y->left,y->right}变为{x->p,x,y->right}
//y->left={y,y->left->left,y->left->right}变为{x,y->left->left,y->left->right}
void Left_Rotate(Os_Tree *T, node *x)
{
//令y = x->right
node *y = x->right;
//按照上面的方式修改三个结点的指针,注意修改指针的顺序
x->right = y->left;
if(y->left != T->nil)
y->left->p = x;
y->p = x->p;
if(x->p == T->nil)//特殊情况:x是根结点
T->root = y;
else if(x == x->p->left)
x->p->left = y;
else
x->p->right = y;
y->left = x;
x->p = y;
//因为旋转而修改size
y->size = x->size;
x->size = x->left->size + x->right->size + 1;
}
//右旋,令y = x->left, 左旋是以x和y之间的链为支轴进行旋转
//旋转过程与上文类似
void Right_Rotate(Os_Tree *T, node *x)
{
node *y = x->left;
x->left = y->right;
if(y->left != T->nil)
y->right->p = x;
y->p = x->p;
if(x->p == T->nil)
T->root = y;
else if(x == x->p->right)
x->p->right = y;
else
x->p->left = y;
y->right = x;
x->p = y;
//因为旋转而修改size
y->size = x->size;
x->size = x->left->size + x->right->size + 1;
}
//红黑树调整
void RB_Insert_Fixup(Os_Tree *T, node *z)
{
node *y;
//唯一需要调整的情况,就是违反性质2的时候,如果不违反性质2,调整结束
while(z->p->color == RED)
{
//p[z]是左孩子时,有三种情况
if(z->p == z->p->p->left)
{
//令y是z的叔结点
y = z->p->p->right;
//第一种情况,z的叔叔y是红色的
if(y->color == RED)
{
//将p[z]和y都着为黑色以解决z和p[z]都是红色的问题
z->p->color = BLACK;
y->color = BLACK;
//将p[p[z]]着为红色以保持性质5
z->p->
7fe8
p->color = RED;
//把p[p[z]]当作新增的结点z来重复while循环
z = z->p->p;
}
else
{
//第二种情况:z的叔叔是黑色的,且z是右孩子
if(z == z->p->right)
{
//对p[z]左旋,转为第三种情况
z = z->p;
Left_Rotate(T, z);
}
//第三种情况:z的叔叔是黑色的,且z是左孩子
//交换p[z]和p[p[z]]的颜色,并右旋
z->p->color = BLACK;
z->p->p->color = RED;
Right_Rotate(T, z->p->p);
}
}
//p[z]是右孩子时,有三种情况,与上面类似
else if(z->p == z->p->p->right)
{
y = z->p->p->left;
if(y->color == RED)
{
z->p->color = BLACK;
y->color = BLACK;
z->p->p->color = RED;
z = z->p->p;
}
else
{
if(z == z->p->left)
{
z = z->p;
Right_Rotate(T, z);
}
z->p->color = BLACK;
z->p->p->color = RED;
Left_Rotate(T, z->p->p);
}
}
}
//根结点置为黑色
T->root->color = BLACK;
}
//向树上加入一个元素
void Os_Insert(Os_Tree *T, node *z)
{
node *y = T->nil, *x = T->root;
//找到应该插入的位置,与二叉查找树的插入相同
while(x != T->nil)
{
y = x;
x->size++;
if(z->key < x->key)
{
//如果插入到x->left中,x->size要+1
x = x->left;
}
else
x = x->right;
}
z->p = y;
if(y == T->nil)
T->root = z;
else if(z->key < y->key)
y->left = z;
else
y->right = z;
z->left = T->nil;
z->right = T->nil;
//将新插入的结点转为红色
z->color = RED;
//从新插入的结点开始,向上调整
RB_Insert_Fixup(T, z);
}
int main()
{
//测试数据,计算数组s的逆序数
int i, sum = 0, n, x;
//输入一组测试数据中有几个数字
while(cin>>n)
{
sum = 0;
//生成一个顺序统计量树
Os_Tree *T = new Os_Tree;
//依次插入结点并计算顺序统计量
for(i = 0; i < n; i++)
{
cin>>x;
//生成一个新的结点
node *z = new node(T->nil, x);
//插入这么结点
Os_Insert(T, z);
//Os_Rank(T, z)表示s[0..i]中<=s[i]和数的个数
//i+1表示s[0..i]总的数的个数
//相减便得到出现在s[i]前但比s[i]大的数的个数
sum = sum + i + 1 - Os_Rank(T, z);
}
cout<<sum<<endl;
delete T;
}
return 0;
}


四、代码测试

内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: