思考与分析:

对于m为常数,能够用循环链表,用head,tail标志头尾指针使其易于表示循环结构。循环输出后删除结点n次,每次外层循环时。内层都固定循环m次。所以执行时间为O(mn)=O(n).

对于m为很数。能够用顺序统计树,用size属性记录每一个结点在当前树中所在的位置。

经过一个取余过程,每次都能正确找到并输出删除每一个结点,在经过n次循环,每次循环都要找到一个结点输出并删除它,所以每次循环都要花费O(lgn)时间。

总的执行时间是O(nlgn).

代码例如以下:

m为常数时:

#include <iostream>
using namespace std;
#define n 10
#define m 3
#define LEN sizeof(struct circular_list)
struct circular_list
{
int key;
struct circular_list* next;
};
struct circular_list*tail=NULL;
struct circular_list*Insert(struct circular_list*&head,int k)
{
struct circular_list*z=new struct circular_list[LEN];
z->key=k;
if (head==NULL)
{
head=tail=z;
head->next=tail;
}
else
{
tail->next=z;
z->next=head;
tail=z;
}
return head;
}
struct circular_list* Delete(struct circular_list*&head,struct circular_list*z)
{
struct circular_list*p=head;
while (p->next!=z)
{
p=p->next;
}
if (head==tail)
{
p->next=NULL;
}
else
{
if (head==p->next)
{
head=p->next->next;
}
else if (tail==p->next)
{
tail=p;
}
p->next=p->next->next;
}
return p->next;
}
void n_m_Josephus(struct circular_list*&head)
{
struct circular_list*p=head;
while (p)
{
int i=0;
while (i!=m-1)
{
p=p->next;
i++;
}
struct circular_list*z=p;
cout<<z->key<<" ";
p=Delete(head,z);
}
}
void main()
{
int a[n]={0};
for (int i=0;i<n;i++)
{
a[i]=i+1;
}
int j=0;
struct circular_list*head=NULL;
while (j!=n)
{
head=Insert(head,a[j]);
j++;;
}
struct circular_list*p=head;
do
{
cout<<p->key;
p=p->next;
} while (p!=head);
cout<<endl;
n_m_Josephus(head);
}

m不是常数时

#include <iostream>
#include <time.h>
using namespace std;
#define BLACK 0
#define RED 1
#define Nil -1
#define LEN sizeof(struct OS_Tree)
struct OS_Tree
{
struct OS_Tree*right,*left;
struct OS_Tree*parent;
int key,color,size;//size表示子树的结点数。 };
struct OS_Tree*root=NULL,*nil=NULL;
void LEFT_ROTATE(struct OS_Tree*x)
{//左旋转:分三个步骤①②③来叙述旋转代码的。
struct OS_Tree*y=x->right;//设置y结点。 x->right=y->left;//本行代码以及以下的if结构表达的是“y的左孩子成为x的右孩子”。①
if(y->left!=nil)
{
y->left->parent=x;
}
y->parent=x->parent;//本行代码以及以下的if-else结构表达的过程是“y成为该子树新的根”。 ②
if(x->parent==nil)
{
root=y;
}
else if(x==x->parent->left)
{
x->parent->left=y;
}
else x->parent->right=y;
y->left=x;//本行代码以及以下一行都表达了“x成为y的左孩子”。③
x->parent=y;
y->size = x->size; //对附加信息的维护
x->size = x->left->size + x->right->size +1;
}
void RIGHT_ROTATE(struct OS_Tree*x)
{//右旋转:分三个步骤①②③来叙述旋转代码的。 struct OS_Tree*y=x->left;//设置y结点。
x->left=y->right;//本行代码以及以下的if结构表达的是“y的左孩子成为x的右孩子”。①
if(y->right!=nil)
{
y->right->parent=x;
}
y->parent=x->parent;//本行代码以及以下的if-else结构表达的过程是“y成为该子树新的根”。②
if(x->parent==nil)
{
root=y;
}
else if(x==x->parent->right)
{
x->parent->right=y;
}
else x->parent->left=y;
y->right=x;//本行代码以及以下一行都表达了“x成为y的左孩子”。③
x->parent=y;
y->size = x->size; //对附加信息的维护
x->size = x->left->size + x->right->size +1;
}
void RB_INSERT_FIXUP(struct OS_Tree*z)
{
while (z->parent->color==RED)
{
if (z->parent==z->parent->parent->left)
{
struct OS_Tree*y=z->parent->parent->right;//叔结点
if (y->color==RED)//情况一:叔结点为红色
{//给p1,y,p2着色以保持性质5。而且攻克了z的父结点和z都是红色结点问题
z->parent->color=BLACK;
y->color=BLACK;
z->parent->parent->color=RED;
z=z->parent->parent;//把z的祖父结点当成新结点z进入下一次循环
}
else
{
if (z==z->parent->right)//情况二:检查z是否是一个右孩子且叔结点为黑色。前提是p1结点不是叶子结点
{//使用一个左旋让情况2转变为情况3
z=z->parent;
LEFT_ROTATE(z);//因为进入if语句后可知旋转结点不可能是叶子结点,这样就不用推断z是否是叶子结点了。
}
z->parent->color=BLACK;//情况三:是z是一个左孩子且叔结点为黑色。改变z的父和祖父结点颜色并做一次右旋,以保持性质5
z->parent->parent->color=RED;
RIGHT_ROTATE(z->parent->parent);//因为p2可能是叶子结点,所以不妨用一个if推断
}
}
else//以下else分支相似于上面,注意到else分支的情况2和情况3所做旋转正好是if分支情况的逆。
{
struct OS_Tree*y=z->parent->parent->left;
if (y->color==RED)
{
z->parent->color=BLACK;
y->color=BLACK;
z->parent->parent->color=RED;
z=z->parent->parent;
}
else
{
if (z==z->parent->left)
{
z=z->parent;
RIGHT_ROTATE(z);
}
z->parent->color=BLACK;
z->parent->parent->color=RED;
LEFT_ROTATE(z->parent->parent);
}
}
}
root->color=BLACK;//最后给根结点着为黑色。 }
void RB_INSERT(struct OS_Tree*z)
{
struct OS_Tree*y=nil;
struct OS_Tree*x=root;
while (x!=nil)
{
x->size++;
y=x;
if (z->key<x->key)
{
x=x->left;
}
else x=x->right;
}
z->parent=y;
if (y==nil)
{
root=z;
}
else if(z->key<y->key)
{
y->left=z;
}
else y->right=z;
z->left=nil;//给插入结点左右孩子赋值为空。
z->right=nil;
z->color=RED;//给插入结点着为红色。
z->size=1;
z->left->size=0;
z->right->size=0;
RB_INSERT_FIXUP(z);
}
void RB_TRANSPLANT(struct OS_Tree*u,struct OS_Tree*v)
{
if (u->parent==nil)
root=v;
else if(u==u->parent->left)
u->parent->left=v;
else u->parent->right=v;
v->parent=u->parent;
}
struct OS_Tree*TREE_MINIMUM(struct OS_Tree*x)//求二叉查找树当前结点最小值
{
while (x!=nil&&x->left!=nil)
{
x=x->left;
}
return x;
}
struct OS_Tree*TREE_MAXINUM(struct OS_Tree*x)//求二叉查找树当前结点最大值
{
while (x!=nil&&x->right!=nil)
{
x=x->right;
}
return x;
}
struct OS_Tree*TREE_PREDECESSOR(struct OS_Tree*x)//查找二叉查找树的前驱
{
if (x->left!=nil)
{
return TREE_MAXINUM(x->left);
}
struct OS_Tree*y=x->parent;
while (y!=nil&&x==y->left)
{
x=y;
y=y->parent;
}
return y;
}
struct OS_Tree*TREE_SUCCESSOR(struct OS_Tree*x)//查找二叉查找树的后继
{
if (x->right!=nil)
{
return TREE_MINIMUM(x->right);
}
struct OS_Tree*y=x->parent;
while (y!=nil&&x==y->right)
{
x=y;
y=y->parent;
}
return y;
}
//非递归版本号的二叉查找树查找函数
struct OS_Tree*ITERATIVE_TREE_SEARCH(struct OS_Tree*x,int k)
{
while (x!=nil&&k!=x->key)
{
if (k<x->key)
{
x=x->left;
}
else x=x->right;
}
return x;
}
void RB_DELETE_FIXUP(struct OS_Tree*x)
{ struct OS_Tree*w=NULL;//w是x的兄弟结点
while (x!=root&&x->color==BLACK)//假设x是黑色而且不是根结点,才进行循环。
{//x是一个具有双重颜色的结点,调整的目的是把x的黑色属性向上移动。 if (x==x->parent->left)
{
w=x->parent->right;
if (w->color==RED)//情况一:x的兄弟结点w是红色的。 {//改变w和x.p的颜色+一次旋转使其变为情况二,三,四。
w->color=BLACK;
x->parent->color=RED;
LEFT_ROTATE(x->parent);
w=x->parent->right;
}
if (w->left->color==BLACK&&w->right->color==BLACK)//情况二:x的兄弟结点w是黑色的,而且w的两个子节点都是黑色。
{
w->color=RED;//从x和w上去掉一重黑色。x还是黑色。而w变为红色。
x=x->parent;//x结点向上移动成为新的待调整结点。 }
else
{
if (w->right->color==BLACK)//情况三:x的兄弟结点w是黑色的。w的左孩子是红色的,w的右孩子是黑色的。
{//交换w和w.left的颜色+旋转使其转变为情况四。
w->left->color=BLACK;
w->color=RED;
RIGHT_ROTATE(w);
w=x->parent->right;
}
w->color=x->parent->color;//以下是情况四:x的兄弟结点w是黑色的,且w的右孩子是红色的。
x->parent->color=BLACK;//置x.p和w.right为黑色+旋转使其去掉x的额外黑色。
w->right->color=BLACK;
LEFT_ROTATE(x->parent);
x=root;//x成为根结点,结束循环。
}
}
else//以下和上面的if分支相似。不做累述。
{
w=x->parent->left;
if (w->color==RED)
{
w->color=BLACK;
x->parent->color=RED;
RIGHT_ROTATE(x->parent);
w=x->parent->left;
}
if (w->left->color==BLACK&&w->right->color==BLACK)
{
w->color=RED;
x=x->parent;
}
else
{
if (w->left->color==BLACK)
{
w->right->color=BLACK;
w->color=RED;
LEFT_ROTATE(w);
w=x->parent->left;
}
w->color=x->parent->color;
x->parent->color=BLACK;
w->left->color=BLACK;
RIGHT_ROTATE(x->parent);
x=root;
}
}
}x->color=BLACK;
}
void RB_DELETE(struct OS_Tree*z)
{
struct OS_Tree*y=z,*x;//y为待删除或待移动结点
int y_original_color=y->color;//保存y的原始颜色,为做最后的调整做准备。
struct OS_Tree*t=z->parent;
if (z->left==nil)
{
while (t!=nil)
{
t->size--;
t=t->parent;
}
x=z->right;//x指向y的唯一子结点或者是叶子结点,保存x的踪迹使其移动到y的原始位置上
RB_TRANSPLANT(z,z->right);//把以z.right为根的子树替换以z为根的子树。
}
else if (z->right==nil)
{
while (t!=nil)
{
t->size--;
t=t->parent;
}
x=z->left;//x指向y的唯一子结点或者是叶子结点。保存x的踪迹使其移动到y的原始位置上
RB_TRANSPLANT(z,z->left);//把以z.left为根的子树替换以z为根的子树。 }
else
{
y=TREE_MINIMUM(z->right);//找到z.right的后继。 struct OS_Tree*t=y->parent;
y->size=z->size-1;//y替换z原来的位置,所以size属性在待删除结点z基础上-1
while (t!=nil)
{
t->size--;
t=t->parent;
}
y_original_color=y->color;//y的新的原始结点被重置。
x=y->right;//x指向y的唯一子结点或者是叶子结点。保存x的踪迹使其移动到y的原始位置上
if (y->parent==z)
{
x->parent=y;//因为z的父结点是要删除的结点,所以不能指向它,于是指向y
}
else
{
RB_TRANSPLANT(y,y->right);//把以y.right为根的子树替换以y为根的子树。 y->right=z->right;
y->right->parent=y;
}
RB_TRANSPLANT(z,y);//把以y为根的子树替换以z为根的子树。
y->left=z->left;
y->left->parent=y;
y->color=z->color;//把已经删除的结点颜色赋值给y。保证了y以上的树结构红黑性质不变。
}
if(y_original_color==BLACK) //y的原始颜色为黑色,说明须要调整红黑颜色。
RB_DELETE_FIXUP(x);
}
//中序遍历
void InOderTraverse(struct OS_Tree *p)
{
if (p!=nil)
{
InOderTraverse(p->left);
cout<<p->key<<" "<<p->color<<" "<<"秩:"<<p->size<<endl;
InOderTraverse(p->right);
}
}
struct OS_Tree*OS_SELECT(struct OS_Tree*&x,int i)//查找顺序统计树给定秩的元素
{
int r=x->left->size+1;
if (i==r)
{
return x;
}
else if (i<r)
{
return OS_SELECT(x->left,i);
}
else return OS_SELECT(x->right,i-r);
}
int ITERATIVE_OS_RANK(struct OS_Tree*&T,struct OS_Tree*x)//确定顺序统计树的秩
{
int r=x->left->size+1;
struct OS_Tree*y=x;
while (y!=root)
{
if (y==y->parent->right)
{
r=r+y->parent->left->size+1;
}
y=y->parent;
}
return r;
}
void n_m_Josephus(struct OS_Tree*x,int m,int n)
{
x=root;
int j=0,i=n,t=1;
while(root!=nil)
{
j=(t+m-1)%i;
if(j==0) j=i;
struct OS_Tree*y1=OS_SELECT(root,j);//y1表示待输出待删除结点
cout<<y1->key<<" ";//输出结点
RB_DELETE(y1);//输出后删除掉
t=j;
i--;
}
}
void main()
{
srand( (unsigned)time( NULL ) );
int m=0,n=0;
cout<<"以下请您输入n_m_Josephus排列的m和n值"<<endl;
cout<<"m=";
cin>>m;
cout<<"n=";
cin>>n;
int *array1=new int[n];
for (int j=0;j<n;j++)
{
array1[j]=j+1;
cout<<array1[j]<<" ";
}
cout<<endl;
nil=new struct OS_Tree[LEN];
nil->key=Nil;nil->color=BLACK;
root=nil;
int i=0;
struct OS_Tree*ROOT=new struct OS_Tree[LEN];
ROOT->key=array1[i++];
RB_INSERT(ROOT);
root=ROOT;
while (i!=n)
{
struct OS_Tree*z=new struct OS_Tree[LEN];
z->key=array1[i];
RB_INSERT(z);
i++;
}
InOderTraverse(root);
cout<<"约瑟夫排列:";
n_m_Josephus(root,m,n);
}

总结:约瑟夫排列a部分用到了第10章知识,而b部分用到了本章知识。这是对学过的知识的简单应用。感觉这个程序还是比較有趣的。

Josephus排列的更多相关文章

  1. josephus 问题的算法(转载)

    Josephus 问题: 一群小孩围成一个圈,任意假定一个数 m,从第一个小孩起,顺时针方向数,每数到第 m 个小孩时,该小孩便离开.小孩不断离开,圈子不断缩小,最后剩下的一个小孩便是胜利者.究竟胜利 ...

  2. Josephus环类问题,java实现

    写出一个双向的循环链表,弄一个计数器,我定义的是到三的时候,自动删除当前节点,很简单. package Com; import java.util.Scanner; /* * 约瑟夫环问题,有n个人组 ...

  3. 学习sql中的排列组合,在园子里搜着看于是。。。

    学习sql中的排列组合,在园子里搜着看,看到篇文章,于是自己(新手)用了最最原始的sql去写出来: --需求----B, C, F, M and S住在一座房子的不同楼层.--B 不住顶层.C 不住底 ...

  4. [LeetCode] Arranging Coins 排列硬币

    You have a total of n coins that you want to form in a staircase shape, where every k-th row must ha ...

  5. [LeetCode] Next Permutation 下一个排列

    Implement next permutation, which rearranges numbers into the lexicographically next greater permuta ...

  6. js学习篇--数组按升序降序排列

    <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8&quo ...

  7. SDOI 2016 排列计数

    题目大意:一个数列A,n个元素,其中m个元素不动,其他元素均不在相应位置,问有多少种排列 保证m个元素不动,组合数学直接计算,剩余元素错位排列一下即可 #include<bits/stdc++. ...

  8. 排列组合算法的javascript实现

    命题:从成员数为N的集合S中,选出M个元素,分别求其排列与组合结果集,即 A(N, M)与C(N, M) js解法: function queue(arr, size){ if(size > a ...

  9. 剑指Offer面试题:26.字符串的排列

    一.题目:字符串的排列 题目:输入一个字符串,打印出该字符串中字符的所有排列.例如输入字符串abc,则打印出由字符a.b.c所能排列出来的所有字符串abc.acb.bac.bca.cab和cba. 二 ...

随机推荐

  1. kong添加upstream

    整理的文档比较早,kong版本可能是0.10.3版本.详情请看官网最新文档 准备 使用kong代理后端请求 1.开放几个接口如下: 本地请求1:http://aaa.wyc.com:8888/aaa ...

  2. 使用Python SocketServer快速实现多线程网络服务器

    Python SocketServer使用介绍 1.简介: SocketServer是python的一个网络服务器框架,可以减少开发人员编写网络服务器程序的工作量. SocketServer总共有4个 ...

  3. Dell最近的几款显示器看上去还不错的样子

    发现最近戴尔最近发布了两款的4k显示器P2415Q和P2715Q,价格还比较平易近人,淘宝价24寸3700,27寸4700,让人有点心动了.基本参数如下: 3840*2160分辨率 亮度350cd/m ...

  4. Linux设备文件简介

    转:http://www.360doc.com/content/11/0418/00/5087210_110410837.shtml 版权声明 本 文作者是一位自由软件爱好者,所以本文虽然不是软件,但 ...

  5. XMPP资源绑定(Resource Binding)与单台设备登录控制

    原文:http://blog.csdn.net/brasbug/article/details/26353511 一个XMPP的账号由三部分组成: 用户名(user/node),域名(domain)和 ...

  6. ocx控件打印之基础篇

      Visual C++6.0是开发Windows应用程序的强大工具,但是要通过它实现程序的打印功能,一直是初学者的一个难点,经常有朋友询问如何在VC中实现打印功能,他们往往感到在MFC提供的框架内实 ...

  7. Xcode no visible @interface for XXX declares

    出现如上面的错误, 是因为没有找到这个方法, 要自己写一个这样的方法 , 如果这是个类目的方法的话,  需要在Target->Linking->Other Linker Flags中添加- ...

  8. Android 卡顿优化 4 布局优化实际技巧

    今天分享一些layout布局书写中的一些技巧,希望看过之后你也一样可以写出性价比高的布局.我个人的目标是用最少的View写出一样效果的布局.因为我相信View的数量减少伴随着的就是层级的减少.从而达到 ...

  9. EasyUI datagrid 双击行事件

    1.EasyUI 实现点击行的任何位置,触发事件 onLoadSuccess:function(data){ } , pagination : false, //双击事件 onDblClickRow: ...

  10. OpenCV支持向量机SVM对线性不可分数据的处理

    支持向量机对线性不可分数据的处理 目标 本文档尝试解答如下问题: 在训练数据线性不可分时,如何定义此情形下支持向量机的最优化问题. 如何设置 CvSVMParams 中的参数来解决此类问题. 动机 为 ...