算法导论 第14章 14.3 区间树

一、综述

1.区间树

区间树中一种对动态集合进行维护的红黑树,该集合中的每个元素x都包含一个区间int[x]

2.基础数据结构

红黑树,其中每个结点x包含一个区间域int[x],x的关键字为区间的低端点

3.附加信息

max[x]:以x为根的子树中所有区间的 端点的最大值

4.对信息的维护

max[x] = max(high[int[x]], max[left[x]], max[right[x]])

5.设计新的操作

INTERVAL-SEARCH(T, i):找出树T中覆盖区间i的那个结点。

二、代码

1.Interval_Tree.h

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

2.main.cpp

[cpp]  view plain copy
  1. #include <iostream>  
  2. #include "Interval_Tree.h"  
  3. using namespace std;  
  4.   
  5. int main()  
  6. {  
  7.     char ch;  
  8.     int x, y;  
  9.     //生成一棵顺序统计树  
  10.     Interval_Tree *T = new Interval_Tree;  
  11.     while(1)  
  12.     {  
  13.         cin>>ch;  
  14.         switch(ch)  
  15.         {  
  16.             //插入一个元素  
  17.         case 'I':  
  18.             {  
  19.                 //cin>>x>>y;  
  20.                 x = rand() % 100;  
  21.                 y = rand() % 100;  
  22.                 if(x > y)swap(x, y);  
  23.                 node *z = new node(T->nil, x, y);  
  24.                 T->Insert(z);  
  25.                 break;  
  26.             }  
  27.             //删除一个元素  
  28.         case 'D':  
  29.             {  
  30.                 cin>>x;  
  31.                 node *z = T->Search(T->root, x);  
  32.                 if(z == NULL)  
  33.                     cout<<"not exist"<<endl;  
  34.                 else  
  35.                     T->Delete(z);  
  36.                 break;  
  37.             }  
  38.             //求第一个与[x,y]相交的区间  
  39.         case 'S':  
  40.             {  
  41.                 cin>>x>>y;  
  42.                 node *z = T->Interval_Search(interval(x, y));  
  43.                 if(z == NULL)  
  44.                     cout<<"not exist"<<endl;  
  45.                 else  
  46.                     cout<<z->inte.low<<' '<<z->inte.high<<endl;  
  47.                 break;  
  48.             }  
  49.         case 'P':  
  50.             T->Print();  
  51.             break;  
  52.         default:  
  53.             break;  
  54.         }  
  55.     }  
  56.     return 0;  
  57. }  



三、练习

14.3-1

[cpp]  view plain copy
  1. LEFT-ROTATE(T, x)  
  2.  1    y <- right[x]  
  3.  2    right[x] <- left[y]  
  4.  3    if left[y] != nil[T]  
  5.  4    then p[left[y]] <- x  
  6.  5    p[y] <- p[x]  
  7.  6    if p[x] = nil[T]  
  8.  7        then root[T] <- y  
  9.  8        else if x = left[p[x]]  
  10.  9            then left[p[x]] <- y  
  11. 10            else right[p[x]] <- y  
  12. 11    left[y] <- x  
  13. 12    p[x] <- y  
  14. 13    max[y] <- max[x]  
  15. 14    max[x] <- max(high[int[x]], max[left[x]], max[right[x]])  

14.3-2

[cpp]  view plain copy
  1. 对二中的程序做三点修改  
  2. (1)L37,<改成<=  
  3. (2)L40,>改成>=  
  4. (3)L443,>=改成>  

14.3-3

那本答案PDF写得比较麻烦,不明天为什么要写得这么复杂,我只分了三种情况
[cpp]  view plain copy
  1. node* Interval_Tree::Search_Min(node *root, interval i)  
  2. {  
  3.     node *x = root, *y;    
  4.     //先从左子树上找  
  5.     if(x->left && x->left->max >= i.low)  
  6.     {  
  7.         y = Search_Min(x->left, i);  
  8.         if(y != nil)  
  9.             return y;  
  10.     }  
  11.     //如果x与i相交,就不需要找左子树了  
  12.     if(Overlap(x->inte, i))  
  13.         return x;  
  14.     //最后在右子树上找  
  15.     if(x->right)  
  16.         return Search_Min(x->right, i);  
  17.     return nil;     
  18. }  

14.3-4

[cpp]  view plain copy
  1. void Interval_Tree::Search_All(node *root, interval i)  
  2. {  
  3.     node *x = root, *y;    
  4.     //如果当前结点与i相交  
  5.     if(Overlap(x->inte, i))  
  6.         cout<<x->inte.low<<' '<<x->inte.high<<endl;  
  7.     //先从左子树上找  
  8.     if(x->left && x->left->max >= i.low)  
  9.         Search_All(x->left, i);  
  10.     //从右子树上找  
  11.     if(x->right && x->key <= i.high)  
  12.         Search_All(x->right, i);    
  13. }  



14.3-5

[cpp]  view plain copy
  1. node* Interval_Tree::Search_Exactly(interval i)  
  2. {  
  3.      //从根结点开始    
  4.     node *x = root;    
  5.     //不是叶子且不重叠    
  6.     while(x != nil)    
  7.     {    
  8.         if(x->inte.low == i.low && x->inte.high == i.high)    
  9.             return x;    
  10.         //在左子树中    
  11.         if(x->left != nil && x->key >= i.low)    
  12.             x = x->left;    
  13.         //在右子树中    
  14.         else    
  15.             x = x->right;    
  16.     }    
  17.     return nil;  
  18. }  


14.3-6

算法导论-14.3-6-MIN-GAP


14.3-7

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

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值