顺序统计树求逆序对 O(nlgn)

来源:互联网 发布:淘宝公益宝贝计划 编辑:程序博客网 时间:2024/05/18 13:24

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

分类: 算法导论 542人阅读 评论(4) 收藏 举报
算法treeinsertosstructnull

目录(?)[+]

一、题目

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

 

二、思考

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

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

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

 

三、代码

[cpp] view plaincopy
  1. #include <iostream>  
  2. using namespace std;  
  3.   
  4. #define BLACK 0  
  5. #define RED 1  
  6.   
  7. //顺序统计量树结点结构  
  8. struct node  
  9. {  
  10.     int key;  
  11.     bool color;  
  12.     node *p;  
  13.     node *left;  
  14.     node *right;  
  15.     int size;//以结点x为根的子树的内部结点的个数,x->key=x->left->key+x->right->key+1  
  16.     node(node *init, int k):left(init),right(init),p(init),key(k),color(BLACK),size(1){}    
  17. };  
  18. //顺序统计量树结构  
  19. struct Os_Tree  
  20. {  
  21.     node *root;  
  22.     node *nil;  
  23.     Os_Tree()  
  24.     {  
  25.         nil = new node(NULL, -1);//哨兵结点  
  26.         root = nil;nil->size = 0;//初始时,树为空,root指向哨兵  
  27.     };    
  28. };  
  29. //计算树T中进行顺序遍历后得到的线性序中x的位置  
  30. int Os_Rank(Os_Tree *T, node *x)  
  31. {  
  32.     //置r为以x为根的子树中key[x]的秩  
  33.     int r = x->left->size + 1;  
  34.     node *y = x;  
  35.     while(y != T->root)  
  36.     {  
  37.         //若y是p[y]的右孩子,p[y]和p[y]左子树中所有结点前于x  
  38.         if(y == y->p->right)  
  39.             r = r + y->p->left->size + 1;  
  40.         y = y->p;  
  41.     }  
  42.     return r;  
  43. }  
  44. //左旋,令y = x->right, 左旋是以x和y之间的链为支轴进行旋转  
  45. //涉及到的结点包括:x,y,y->left,令node={p,l,r},具体变化如下:  
  46. //x={x->p,x->left,y}变为{y,x->left,y->left}  
  47. //y={x,y->left,y->right}变为{x->p,x,y->right}  
  48. //y->left={y,y->left->left,y->left->right}变为{x,y->left->left,y->left->right}  
  49. void Left_Rotate(Os_Tree *T, node *x)  
  50. {  
  51.     //令y = x->right  
  52.     node *y = x->right;  
  53.     //按照上面的方式修改三个结点的指针,注意修改指针的顺序  
  54.     x->right = y->left;  
  55.     if(y->left != T->nil)  
  56.         y->left->p = x;  
  57.     y->p = x->p;  
  58.     if(x->p == T->nil)//特殊情况:x是根结点  
  59.         T->root = y;  
  60.     else if(x == x->p->left)  
  61.         x->p->left = y;  
  62.     else   
  63.         x->p->right = y;  
  64.     y->left = x;  
  65.     x->p = y;  
  66.     //因为旋转而修改size  
  67.     y->size = x->size;  
  68.     x->size = x->left->size + x->right->size + 1;  
  69. }  
  70. //右旋,令y = x->left, 左旋是以x和y之间的链为支轴进行旋转  
  71. //旋转过程与上文类似  
  72. void Right_Rotate(Os_Tree *T, node *x)  
  73. {  
  74.     node *y = x->left;  
  75.     x->left = y->right;  
  76.     if(y->left != T->nil)  
  77.         y->right->p = x;  
  78.     y->p = x->p;  
  79.     if(x->p == T->nil)  
  80.         T->root = y;  
  81.     else if(x == x->p->right)  
  82.         x->p->right = y;  
  83.     else   
  84.         x->p->left = y;  
  85.     y->right = x;  
  86.     x->p = y;  
  87.     //因为旋转而修改size  
  88.     y->size = x->size;  
  89.     x->size = x->left->size + x->right->size + 1;  
  90. }  
  91. //红黑树调整  
  92. void RB_Insert_Fixup(Os_Tree *T, node *z)  
  93. {  
  94.     node *y;  
  95.     //唯一需要调整的情况,就是违反性质2的时候,如果不违反性质2,调整结束  
  96.     while(z->p->color == RED)  
  97.     {  
  98.         //p[z]是左孩子时,有三种情况  
  99.         if(z->p == z->p->p->left)  
  100.         {  
  101.             //令y是z的叔结点  
  102.             y = z->p->p->right;  
  103.             //第一种情况,z的叔叔y是红色的  
  104.             if(y->color == RED)  
  105.             {  
  106.                 //将p[z]和y都着为黑色以解决z和p[z]都是红色的问题  
  107.                 z->p->color = BLACK;  
  108.                 y->color = BLACK;  
  109.                 //将p[p[z]]着为红色以保持性质5  
  110.                 z->p->p->color = RED;  
  111.                 //把p[p[z]]当作新增的结点z来重复while循环  
  112.                 z = z->p->p;  
  113.             }  
  114.             else  
  115.             {  
  116.                 //第二种情况:z的叔叔是黑色的,且z是右孩子  
  117.                 if(z == z->p->right)  
  118.                 {  
  119.                     //对p[z]左旋,转为第三种情况  
  120.                     z = z->p;  
  121.                     Left_Rotate(T, z);  
  122.                 }  
  123.                 //第三种情况:z的叔叔是黑色的,且z是左孩子  
  124.                 //交换p[z]和p[p[z]]的颜色,并右旋  
  125.                 z->p->color = BLACK;  
  126.                 z->p->p->color = RED;  
  127.                 Right_Rotate(T, z->p->p);  
  128.             }  
  129.         }  
  130.         //p[z]是右孩子时,有三种情况,与上面类似  
  131.         else if(z->p == z->p->p->right)  
  132.         {  
  133.             y = z->p->p->left;  
  134.             if(y->color == RED)  
  135.             {  
  136.                 z->p->color = BLACK;  
  137.                 y->color = BLACK;  
  138.                 z->p->p->color = RED;  
  139.                 z = z->p->p;  
  140.             }  
  141.             else  
  142.             {  
  143.                 if(z == z->p->left)  
  144.                 {  
  145.                     z = z->p;  
  146.                     Right_Rotate(T, z);  
  147.                 }  
  148.                 z->p->color = BLACK;  
  149.                 z->p->p->color = RED;  
  150.                 Left_Rotate(T, z->p->p);  
  151.             }  
  152.         }  
  153.     }  
  154.     //根结点置为黑色  
  155.     T->root->color = BLACK;  
  156. }  
  157. //向树上加入一个元素  
  158. void Os_Insert(Os_Tree *T, node *z)  
  159. {  
  160.     node *y = T->nil, *x = T->root;  
  161.     //找到应该插入的位置,与二叉查找树的插入相同  
  162.     while(x != T->nil)  
  163.     {  
  164.         y = x;  
  165.         x->size++;  
  166.         if(z->key < x->key)  
  167.         {  
  168.             //如果插入到x->left中,x->size要+1  
  169.             x = x->left;  
  170.         }  
  171.         else  
  172.             x = x->right;  
  173.     }  
  174.     z->p = y;  
  175.     if(y == T->nil)  
  176.         T->root = z;  
  177.     else if(z->key < y->key)  
  178.         y->left = z;  
  179.     else  
  180.         y->right = z;  
  181.     z->left = T->nil;  
  182.     z->right = T->nil;  
  183.     //将新插入的结点转为红色  
  184.     z->color = RED;  
  185.     //从新插入的结点开始,向上调整  
  186.     RB_Insert_Fixup(T, z);  
  187. }  
  188. int main()  
  189. {  
  190.     //测试数据,计算数组s的逆序数  
  191.     int i, sum = 0, n, x;  
  192.     //输入一组测试数据中有几个数字  
  193.     while(cin>>n)  
  194.     {  
  195.         sum = 0;  
  196.         //生成一个顺序统计量树  
  197.         Os_Tree *T = new Os_Tree;  
  198.         //依次插入结点并计算顺序统计量  
  199.         for(i = 0; i < n; i++)  
  200.         {  
  201.             cin>>x;  
  202.             //生成一个新的结点  
  203.             node *z = new node(T->nil, x);  
  204.             //插入这么结点  
  205.             Os_Insert(T, z);  
  206.             //Os_Rank(T, z)表示s[0..i]中<=s[i]和数的个数  
  207.             //i+1表示s[0..i]总的数的个数  
  208.             //相减便得到出现在s[i]前但比s[i]大的数的个数  
  209.             sum = sum + i + 1 - Os_Rank(T, z);  
  210.         }  
  211.         cout<<sum<<endl;  
  212.         delete T;  
  213.     }  
  214.     return 0;  
  215. }  


四、代码测试

原创粉丝点击