算法导论-14.3-7-O(nlgn)时间求矩形集合中重叠矩形的个数

本文介绍一种利用红黑树扩展的区间树数据结构,在O(nlgn)时间内找出一组矩形中相互重叠的矩形对数量的方法。首先对矩形的x坐标进行排序,然后通过区间树维护矩形的y区间,以此来计算重叠矩形的数量。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

算法导论-14.3-7-O(nlgn)时间求矩形集合中重叠矩形的个数

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

目录(?)[+]

一、题目



二、思考

采用红黑树作为基础数据结构,扩展为14.3中的区间树。

第一步:

对每个矩形的左边x和右边x排序,排序结果放在一个序列中。并记录每个x值属于哪个矩形。

如三个矩形的x区间(左边x,右边x)分别是(1,3),(2,4),(3,5),排序结果(x值,属于哪个矩形)是(1,1),(2,2),(3,1),(3,3),(4,2),(5,3)。

第二步:

依次处理排序结果(x,i)。

(1)如果x是矩形i的左边x,则求出区间树中与矩形i的y区间相交的区间个数,再把矩形i的y区间插入到区间树中。

(2)如果x是矩形i的右边x,则将矩形i的y区间从区间树中删除


三、代码

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值