算法导论-14-2-Josephus排列

算法导论-14-2-Josephus排列

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

题目:

Josephus问题的定义如下:假设n个人排成环形,且有以正整数m<=n。从某个制定的人开始,沿环报数,每遇到第m个人就让其出列,且报数进行下去。这个过程一直进行到所有人都出列为止。每个人出列的次序定义了整数1,2,...,n的(n, m)-Josephus排列。例如,(7,3)-Josephus排列为<3,6,2,7,5,1,4>。
a)假设m为整数。请描述一个O(n)时间的算法,使之对给定的整数n,输出(n, m)-Josephus排列。
b)假设m不是个常数。请描述一个O(nlgn)时间的算法,使给定的整数n和m,输出(n, m)-Josephus排列。


 思考:

利用14.1中的动态顺序统计,假设刚刚删除的是剩余点中的第start个点(初始时为0),此时还剩下t个点,那么下一个要删除的是剩余点的第(start+m-1)%t个点

步骤1:基础数据结构

红黑树

步骤2:附加信息

size[x]:以x为根的子树的(内部)结点数(包括x本身),即子树的大小。size[nil[T]]=0

步骤3:对信息的维护

size[x] = size[left[x]] + size[right[x]] + 1

插入操作:从插入结点到根结点都要更新O(lgn)

旋转操作:只需要更新旋转轴上的两个点O(1)

删除操作:从删除结点的父结点开始到根结点都要更新O(lgn)

代码:

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值