算法导论 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; }
四、代码测试
相关文章推荐
- 算法导论-15-4-计划一个公司聚会
- 算法导论-13-1-持久动态集合
- Linux2.6为数据结构分配内存-slab
- 算法导论-15-7-达到最高效益的调度
- 算法导论 第10章 10.4 有根树的表示
- 算法导论 第13章 红黑树
- 算法导论 第7章 快速排序
- 算法导论 第12章 二叉查找树
- 算法导论-24.2-有向无回路图中的单源最短路径
- 算法导论 第10章 10.1 栈和队列
- 算法导论 11.1-4 大数组的直接寻址表
- 算法导论-14-1-最大重叠点
- 算法导论-12-2-基数树
- 算法导论-14-2-Josephus排列
- 算法导论-8-3-排序不同长度的数据项
- 算法导论 10.4-2 O(n)时间 递归遍历二叉树
- 算法导论-9.3-3-快速排序-最坏时间O(nlgn)
- 算法导论-24.1-Bellman-Ford算法
- 算法导论-15-1-双调欧几里得旅行商问题
- 算法导论-13-3-AVL树