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

 

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

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

目录(?)[+]

一、题目

请说明如何维护一个支持操作MIN-GAP的动态数据集Q,使得该操作能够给出Q中两个数之间的最小差幅。例如,Q={1,5,9,15,18,22},则MIN-GAP(Q)返回18-15=3,因为15和18是其中最近的两个数。使用操作INSERT,DELETE,SEARCH和MIN-GAP尽可能高效,并分析它们的运行时间。


二、思考

步骤1:基础数据结构

红黑树,数组中的数值分别作为每个结点的关键字

步骤2:附加信息

min-gap[x]:记录以x为根结点的树的min-gap。当x为叶子结点时,min-gap[x]=0x7fffffff

min-val[x]:记录以x为根结点的树中最小的关键字

max-val[x]:记录以x为根结点的树中最大的关键字

步骤3:对信息的维护

在插入的删除的同时,对这三个附加信息进行更新操作,时间复杂度不改变

算法导论-14.3-6-MIN-GAP_第1张图片

步骤4:设计新的操作

Min_Gap():返回根结点的min-gap值

 

三、代码

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


四、测试代码

算法导论-14.3-6-MIN-GAP_第2张图片

你可能感兴趣的:(算法导论)