算法导论-14-1-最大重叠点

算法导论-14-1-最大重叠点

分类: 算法导论   479人阅读  评论(0)  收藏  举报

题目:

假设希望对一组区间记录一个最大重叠点,亦即覆盖它的区间最多的那个点。
a)证明:最大重叠点总存在于某段的端点上。
b)设计一数据结构,能有效地支持操作INTERVAL-INSERT,INTERVAL-DELETE和返回最大重叠点操作FIND-POM。(提示:将所有端点组织成红黑树。左端点关联+1值,而右端点关联-1值。附加一些维护最大重叠点的信息以扩张树中结点。)

思考:

设有n个区间,将所有2n个点从小到大排序,对于排序后的第i个点,若它是某个区间的左端点,则p[i]=1,若它是某个区间的右端点,则p[i]=-1。由第一问可知,所求的点一定是某条线段的端点,所以从端点集合中找出被最多区间覆盖的那个。若一个端点是排序后的第i个点,则有个SUM(s[1],s[i])个区间覆盖这个点。

使用红黑树对所有的端点进行动态排序并保证有较好的性质,在树的结点中增加一些顺序统计量的信息,用于求SUM(s[1],s[i])

步骤1:基础数据结构

红黑树,p[x]=1表示它是区间的左端点,p[x]=-1表示它是区间的右端点

步骤2:附加信息

v[x]:以x为根的所有结点的p值之和

m[x]:MAX(SUM([left[x], i))

o[x]:以x为根的所有结点中的最大覆盖点

步骤3:对信息的维护

步骤4:设计新的操作

Find_Pom():返回根结点的p值

代码:

[cpp]  view plain copy
  1. #include <iostream>  
  2. using namespace std;  
  3.   
  4. #define BLACK 0  
  5. #define RED 1  
  6.   
  7. //红黑树结点结构  
  8. struct node  
  9. {  
  10.     //红黑树的基础结构  
  11.     node *left;  
  12.     node *right;  
  13.     node *parent;  
  14.     int key;  
  15.     bool color;  
  16.     int p;//1:左端点;-1:区间右端点  
  17.     //附加信息  
  18.     int v;  
  19.     int m;  
  20.     int o;  
  21.     node(node *init, int k)  
  22.         :left(init),right(init),parent(init),key(k),color(BLACK),v(0),m(0),o(k){}  
  23. };  
  24. //红黑树结构  
  25. struct Red_Black_Tree  
  26. {  
  27.     node *root;//根结点  
  28.     node *nil;//哨兵  
  29.     Red_Black_Tree(){nil = new node(NULL, -1);root = nil;};  
  30. };  
  31. int max(int a, int b, int c)  
  32. {  
  33.     if(a > b)  
  34.         return a > c ? a : c;  
  35.     else  
  36.         return b > c ? b : c;  
  37. }  
  38. //对附加信息的维护  
  39. void Maintaining(node *x)  
  40. {  
  41.     while(x->key >= 0)  
  42.     {  
  43.         x->v = x->left->v + x->p + x->right->v;  
  44.         x->m = max(x->left->v,  
  45.             x->left->v + x->p,  
  46.             x->left->v + x->p + x->right->m);  
  47.         if(x->left->key != -1 && x->m == x->left->v)  
  48.             x->o = x->left->o;  
  49.         else if(x->right->key != -1 && x->m == x->left->v + x->p + x->right->m)  
  50.             x->o = x->right->o;  
  51.         else  
  52.             x->o = x->key;  
  53.         x = x->parent;  
  54.     }  
  55. }  
  56. //左旋,令y = x->right, 左旋是以x和y之间的链为支轴进行旋转  
  57. //涉及到的结点包括:x,y,y->left,令node={p,l,r},具体变化如下:  
  58. //x={x->parent,x->left,y}变为{y,x->left,y->left}  
  59. //y={x,y->left,y->right}变为{x->parent,x,y->right}  
  60. //y->left={y,y->left->left,y->left->right}变为{x,y->left->left,y->left->right}  
  61. void Left_Rotate(Red_Black_Tree *T, node *x)  
  62. {  
  63.     //令y = x->right  
  64.     node *y = x->right;  
  65.     //按照上面的方式修改三个结点的指针,注意修改指针的顺序  
  66.     x->right = y->left;  
  67.     if(y->left != T->nil)  
  68.         y->left->parent = x;  
  69.     y->parent = x->parent;  
  70.     if(x->parent == T->nil)//特殊情况:x是根结点  
  71.         T->root = y;  
  72.     else if(x == x->parent->left)  
  73.         x->parent->left = y;  
  74.     else   
  75.         x->parent->right = y;  
  76.     y->left = x;  
  77.     x->parent = y;  
  78.     //对附加信息的维护  
  79.     Maintaining(x);  
  80.     Maintaining(y);  
  81. }  
  82. //右旋,令y = x->left, 左旋是以x和y之间的链为支轴进行旋转  
  83. //旋转过程与上文类似  
  84. void Right_Rotate(Red_Black_Tree *T, node *x)  
  85. {  
  86.     node *y = x->left;  
  87.     x->left = y->right;  
  88.     if(y->right != T->nil)  
  89.         y->right->parent = x;  
  90.     y->parent = x->parent;  
  91.     if(x->parent == T->nil)  
  92.         T->root = y;  
  93.     else if(x == x->parent->right)  
  94.         x->parent->right = y;  
  95.     else   
  96.         x->parent->left = y;  
  97.     y->right = x;  
  98.     x->parent = y;  
  99.     //对附加信息的维护  
  100.     Maintaining(x);  
  101.     Maintaining(y);  
  102. }  
  103. //红黑树调整  
  104. void RB_Insert_Fixup(Red_Black_Tree *T, node *z)  
  105. {  
  106.     node *y;  
  107.     //唯一需要调整的情况,就是违反性质2的时候,如果不违反性质2,调整结束  
  108.     while(z->parent->color == RED)  
  109.     {  
  110.         //parent[z]是左孩子时,有三种情况  
  111.         if(z->parent == z->parent->parent->left)  
  112.         {  
  113.             //令y是z的叔结点  
  114.             y = z->parent->parent->right;  
  115.             //第一种情况,z的叔叔y是红色的  
  116.             if(y->color == RED)  
  117.             {  
  118.                 //将parent[z]和y都着为黑色以解决z和parent[z]都是红色的问题  
  119.                 z->parent->color = BLACK;  
  120.                 y->color = BLACK;  
  121.                 //将parent[parent[z]]着为红色以保持性质5  
  122.                 z->parent->parent->color = RED;  
  123.                 //把parent[parent[z]]当作新增的结点z来重复while循环  
  124.                 z = z->parent->parent;  
  125.             }  
  126.             else  
  127.             {  
  128.                 //第二种情况:z的叔叔是黑色的,且z是右孩子  
  129.                 if(z == z->parent->right)  
  130.                 {  
  131.                     //对parent[z]左旋,转为第三种情况  
  132.                     z = z->parent;  
  133.                     Left_Rotate(T, z);  
  134.                 }  
  135.                 //第三种情况:z的叔叔是黑色的,且z是左孩子  
  136.                 //交换parent[z]和parent[parent[z]]的颜色,并右旋  
  137.                 z->parent->color = BLACK;  
  138.                 z->parent->parent->color = RED;  
  139.                 Right_Rotate(T, z->parent->parent);  
  140.             }  
  141.         }  
  142.         //parent[z]是右孩子时,有三种情况,与上面类似  
  143.         else if(z->parent == z->parent->parent->right)  
  144.         {  
  145.             y = z->parent->parent->left;  
  146.             if(y->color == RED)  
  147.             {  
  148.                 z->parent->color = BLACK;  
  149.                 y->color = BLACK;  
  150.                 z->parent->parent->color = RED;  
  151.                 z = z->parent->parent;  
  152.             }  
  153.             else  
  154.             {  
  155.                 if(z == z->parent->left)  
  156.                 {  
  157.                     z = z->parent;  
  158.                     Right_Rotate(T, z);  
  159.                 }  
  160.                 z->parent->color = BLACK;  
  161.                 z->parent->parent->color = RED;  
  162.                 Left_Rotate(T, z->parent->parent);  
  163.             }  
  164.         }  
  165.     }  
  166.     //根结点置为黑色  
  167.     T->root->color = BLACK;  
  168. }  
  169. //插入一个结点  
  170. void RB_Insert(Red_Black_Tree *T, node *z)  
  171. {  
  172.     node *y = T->nil, *x = T->root;  
  173.     //找到应该插入的位置,与二叉查找树的插入相同  
  174.     while(x != T->nil)  
  175.     {  
  176.         y = x;  
  177.         if(z->key < x->key)  
  178.             x = x->left;  
  179.         else if(z->key > x->key)  
  180.             x = x->right;  
  181.     }  
  182.     z->parent = y;  
  183.     if(y == T->nil)  
  184.         T->root = z;  
  185.     else if(z->key < y->key)  
  186.         y->left = z;  
  187.     else  
  188.         y->right = z;  
  189.     z->left = T->nil;  
  190.     z->right = T->nil;  
  191.     //将新插入的结点转为红色  
  192.     z->color = RED;  
  193.     //从新插入的结点开始,向上调整  
  194.     RB_Insert_Fixup(T, z);  
  195.     Maintaining(z);  
  196. }  
  197. //对树进行调整,x指向一个红黑结点,调整的过程是将额外的黑色沿树上移  
  198. void RB_Delete_Fixup(Red_Black_Tree *T, node *x)  
  199. {  
  200.     node *w;  
  201.     //如果这个额外的黑色在一个根结点或一个红结点上,结点会吸收额外的黑色,成为一个黑色的结点  
  202.     while(x != T->root && x->color == BLACK)  
  203.     {  
  204.         //若x是其父的左结点(右结点的情况相对应)  
  205.         if(x == x->parent->left)  
  206.         {  
  207.             //令w为x的兄弟,根据w的不同,分为三种情况来处理  
  208.             //执行删除操作前x肯定是没有兄弟的,执行删除操作后x肯定是有兄弟的  
  209.             w = x->parent->right;  
  210.             //第一种情况:w是红色的  
  211.             if(w->color == RED)  
  212.             {  
  213.                 //改变w和parent[x]的颜色  
  214.                 w->color = BLACK;  
  215.                 x->parent->color = RED;  
  216.                 //对parent[x]进行一次左旋  
  217.                 Left_Rotate(T, x->parent);  
  218.                 //令w为x的新兄弟  
  219.                 w = x->parent->right;  
  220.                 //转为2.3.4三种情况之一  
  221.             }  
  222.             //第二情况:w为黑色,w的两个孩子也都是黑色  
  223.             if(w->left->color == BLACK && w->right->color == BLACK)  
  224.             {  
  225.                 //去掉w和x的黑色  
  226.                 //w只有一层黑色,去掉变为红色,x有多余的一层黑色,去掉后恢复原来颜色  
  227.                 w->color = RED;  
  228.                 //在parent[x]上补一层黑色  
  229.                 x = x->parent;  
  230.                 //现在新x上有个额外的黑色,转入for循环继续处理  
  231.             }  
  232.             //第三种情况,w是黑色的,w->left是红色的,w->right是黑色的  
  233.             else  
  234.             {  
  235.                 if(w->right->color == BLACK)  
  236.                 {  
  237.                     //改变w和left[x]的颜色  
  238.                     w->left->color = BLACK;  
  239.                     w->color = RED;  
  240.                     //对w进行一次右旋  
  241.                     Right_Rotate(T, w);  
  242.                     //令w为x的新兄弟  
  243.                     w = x->parent->right;  
  244.                     //此时转变为第四种情况  
  245.                 }  
  246.                 //第四种情况:w是黑色的,w->left是黑色的,w->right是红色的  
  247.                 //修改w和parent[x]的颜色  
  248.                 w->color =x->parent->color;  
  249.                 x->parent->color = BLACK;  
  250.                 w->right->color = BLACK;  
  251.                 //对parent[x]进行一次左旋  
  252.                 Left_Rotate(T, x->parent);  
  253.                 //此时调整已经结束,将x置为根结点是为了结束循环  
  254.                 x = T->root;  
  255.             }  
  256.         }  
  257.         //若x是其父的左结点(右结点的情况相对应)  
  258.         else if(x == x->parent->right)  
  259.         {  
  260.             //令w为x的兄弟,根据w的不同,分为三种情况来处理  
  261.             //执行删除操作前x肯定是没有兄弟的,执行删除操作后x肯定是有兄弟的  
  262.             w = x->parent->left;  
  263.             //第一种情况:w是红色的  
  264.             if(w->color == RED)  
  265.             {  
  266.                 //改变w和parent[x]的颜色  
  267.                 w->color = BLACK;  
  268.                 x->parent->color = RED;  
  269.                 //对parent[x]进行一次左旋  
  270.                 Right_Rotate(T, x->parent);  
  271.                 //令w为x的新兄弟  
  272.                 w = x->parent->left;  
  273.                 //转为2.3.4三种情况之一  
  274.             }  
  275.             //第二情况:w为黑色,w的两个孩子也都是黑色  
  276.             if(w->right->color == BLACK && w->left->color == BLACK)  
  277.             {  
  278.                 //去掉w和x的黑色  
  279.                 //w只有一层黑色,去掉变为红色,x有多余的一层黑色,去掉后恢复原来颜色  
  280.                 w->color = RED;  
  281.                 //在parent[x]上补一层黑色  
  282.                 x = x->parent;  
  283.                 //现在新x上有个额外的黑色,转入for循环继续处理  
  284.             }  
  285.             //第三种情况,w是黑色的,w->right是红色的,w->left是黑色的  
  286.             else  
  287.             {  
  288.                 if(w->left->color == BLACK)  
  289.                 {  
  290.                     //改变w和right[x]的颜色  
  291.                     w->right->color = BLACK;  
  292.                     w->color = RED;  
  293.                     //对w进行一次右旋  
  294.                     Left_Rotate(T, w);  
  295.                     //令w为x的新兄弟  
  296.                     w = x->parent->left;  
  297.                     //此时转变为第四种情况  
  298.                 }  
  299.                 //第四种情况:w是黑色的,w->right是黑色的,w->left是红色的  
  300.                 //修改w和parent[x]的颜色  
  301.                 w->color =x->parent->color;  
  302.                 x->parent->color = BLACK;  
  303.                 w->left->color = BLACK;  
  304.                 //对parent[x]进行一次左旋  
  305.                 Right_Rotate(T, x->parent);  
  306.                 //此时调整已经结束,将x置为根结点是为了结束循环  
  307.                 x = T->root;  
  308.             }  
  309.         }  
  310.     }  
  311.     //吸收了额外的黑色  
  312.     x->color = BLACK;  
  313. }  
  314. //找最小值     
  315. node *Tree_Minimum(Red_Black_Tree *T, node *x)    
  316. {    
  317.     //只要有比当前结点小的结点     
  318.     while(x->left != T->nil)    
  319.         x = x->left;    
  320.     return x;    
  321. }   
  322. //查找中序遍历下x结点的后继,后继是大于key[x]的最小的结点     
  323. node *Tree_Successor(Red_Black_Tree *T, node *x)    
  324. {    
  325.     //如果有右孩子     
  326.     if(x->right != T->nil)    
  327.         //右子树中的最小值     
  328.         return Tree_Minimum(T, x->right);    
  329.     //如果x的右子树为空且x有后继y,那么y是x的最低祖先结点,且y的左儿子也是     
  330.     node *y = x->parent;    
  331.     while(y != NULL && x == y->right)    
  332.     {    
  333.         x = y;    
  334.         y = y->parent;    
  335.     }    
  336.     return y;    
  337. }    
  338. //递归地查询二叉查找树     
  339. node *RB_Search(node *x, int k)    
  340. {    
  341.     //找到叶子结点了还没找到,或当前结点是所查找的结点     
  342.     if(x->key == -1 || k == x->key)    
  343.         return x;    
  344.     //所查找的结点位于当前结点的左子树     
  345.     if(k < x->key)    
  346.         return RB_Search(x->left, k);    
  347.     //所查找的结点位于当前结点的左子树     
  348.     else    
  349.         return RB_Search(x->right, k);    
  350. }   
  351. //红黑树的删除  
  352. node *RB_Delete(Red_Black_Tree *T, node *z)  
  353. {  
  354.     //找到结点的位置并删除,这一部分与二叉查找树的删除相同  
  355.     node *x, *y;  
  356.     if(z->left == T->nil || z->right == T->nil)  
  357.         y = z;  
  358.     else y = Tree_Successor(T, z);  
  359.     if(y->left != T->nil)  
  360.         x = y->left;  
  361.     else x = y->right;  
  362.     x->parent = y->parent;  
  363.     if(y->parent == T->nil)  
  364.         T->root = x;  
  365.     else if(y == y->parent->left)  
  366.         y->parent->left = x;  
  367.     else  
  368.         y->parent->right = x;  
  369.     Maintaining(y->parent);  
  370.     if(y != z)  
  371.     {  
  372.         z->key = y->key;  
  373.         z->p = y->p;  
  374.         Maintaining(z);  
  375.     }  
  376.     //如果被删除的结点是黑色的,则需要调整  
  377.     if(y->color == BLACK)  
  378.         RB_Delete_Fixup(T, x);  
  379.     return y;  
  380. }  
  381. //求最大覆盖点  
  382. int Find_Pom(Red_Black_Tree *T)  
  383. {  
  384.     return T->root->o;  
  385. }  
  386. int main()  
  387. {  
  388.     //生成一棵树  
  389.     Red_Black_Tree *T = new Red_Black_Tree;  
  390.     char ch;  
  391.     int start, end;  
  392.     node *ret;  
  393.     while(1)  
  394.     {  
  395.         cin>>ch;  
  396.         switch (ch)  
  397.         {  
  398.             //插入一个区间  
  399.         case 'I':  
  400.             {  
  401.             start = rand() % 100;  
  402.             end = rand() % 100;  
  403.             if(start > end)  
  404.                 swap(start, end);  
  405.             cout<<start<<' '<<end<<endl;  
  406.             //若端点已存在,不需要再加入,只需要对p做+1或-1操作  
  407.             ret = RB_Search(T->root, start);  
  408.             if(ret != T->nil)  
  409.                 ret->p++;  
  410.             //若端点不存在,则加入  
  411.             else  
  412.             {  
  413.                 ret = new node(T->nil, start);  
  414.                 ret->p = 1;  
  415.                 RB_Insert(T, ret);  
  416.             }  
  417.             //对右端点操作相似  
  418.             ret = RB_Search(T->root, end);  
  419.             if(ret != T->nil)  
  420.                 ret->p--;  
  421.             else  
  422.             {  
  423.                 ret = new node(T->nil, end);  
  424.                 ret->p = -1;  
  425.                 RB_Insert(T, ret);  
  426.             }  
  427.             //输出结果  
  428.             cout<<Find_Pom(T)<<endl;  
  429.             break;  
  430.             }  
  431.             //删除一个区间,即做与插入相反的操作  
  432.         case 'D':  
  433.             cin>>start>>end;  
  434.             ret = RB_Search(T->root, start);  
  435.             if(ret != T->nil)  
  436.             {  
  437.                 //左端对应的结点p-1  
  438.                 ret->p--;  
  439.                 if(ret->p == 0)  
  440.                     RB_Delete(T, ret);  
  441.             }  
  442.             ret = RB_Search(T->root, end);  
  443.             if(ret != T->nil)  
  444.             {  
  445.                 //右端对应的结点p+1  
  446.                 ret->p++;  
  447.                 if(ret->p == 0)  
  448.                     RB_Delete(T, ret);  
  449.             }  
  450.             //输出结果  
  451.             cout<<Find_Pom(T)<<endl;  
  452.             break;  
  453.         }  
  454.     }  
  455.     return 0;  
  456. }  
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值