约瑟夫环问题是一个很经典的问题:一个圈共有N个人(N为不确定的数字),第一个人的编号为0或者1(两个都可以,看你的程序如何编写),假设这边我将第一个人的编号设置为1号,那么第二个人的编号就为2号,第三个人的编号就为3号,第N个人的编号就为N号,现在提供一个数字M,第一个人开始从1报数,第二个人报的数就是2,依次类推,报到M这个数字的人出局,紧接着从出局的这个人的下一个人重新开始从1报数,和上面过程类似,报到M的人出局,直到N个人全部出局,请问,这个出局的顺序是什么?
举一个简单的例子:假设现在N的值为10,代表有10个人,M的值为3,代表报数报到3的人出局,那么出局的顺序就为:3 6 9 2 7 1 8 5 10 4

分析:
如上图所示,圈内矩形格子中的数字代表每个人的编号,从1开始编号到10。圈外半椭圆中的数字代表10个人的出局顺序。
注意:已经出局的人无需报数,报数的都是未出局的人。
从第一个人开始报数,报到3的人出局,因此,第一个出局的人为3号,3号出局之后,要从出局的这个人(3号)的下一个未出局的人(4号)重新开始从1开始报数,所以4号从1开始继续报数,那么,第二个出局的人就是6号,6号出局之后,要从出局的这个人(6号)的下一个未出局的人(7号)重新开始从1开始报数,所以7号从1开始继续报数,那么,第三个出局的人就是9号,9号出局之后,要从出局的这个人(9号)的下一个未出局的人(10号)重新开始从1开始报数,所以10号从1开始继续报数,那么第四个出局的人就是2号(10号报1,1号报2,2号报3,2号出局),2号出局之后,要从出局的这个人(2号)的下一个未出局的人(4号,这边3号已经出局了,不能报数,所以直接跳到4号)重新开始从1开始报数,那么第五个出局的人就是7号,7号出局之后,要从出局的这个人(7号)的下一个未出局的人(8号)重新开始从1开始报数,那么第六个出局的人就是1号,1号出局之后,要从出局的这个人的下一个未出局的人(4号)重新开始从1开始报数,那么第七个出局的人就是8号,8号出局之后,要从出局的这个人(8号)的下一个未出局的人(10号)重新开始从1 开始报数,那么第八个出局的人就是5号,5号出局之后,要从出局的这个人(5号)的下一个未出局的人(10号)重新开始从1开始报数,那么第九个出局的就是10号,10号出局之后,要从出局的这个人(10号)的下一个未出局的人(4号)重新开始从1开始报数,此时,N个人从只剩下4号还未出局,4号自己从1开始报数,自己数到3,那么它也出局了,4号是第十个出局的人。
思路:构建单向循环链表
1.构建链表结构体
typedef struct node
{
data_t data;
struct node *next;
}linknode,*linklist;
2.构建链表功能函数
//创建链表(无头结点)
linklist list_create(int indata){
linklist H=(linklist)malloc(sizeof(linknode));
if(NULL==H){
printf("create fail\n");
return NULL;
}
H->data=indata;
H->next=H;
return H;
}
//尾插法插入链表
int list_tail_insert(linklist H,data_t value){
if(NULL == H){
puts("入参非法!");
return -1;
}
linklist New=(linklist)malloc(sizeof(linknode));
if(NULL == New){
puts("创建新节点失败!");
return -1;
}
New->data = value;
New->next=NULL;
linklist q=H;
while(q->next!=H){
q=q->next;
}
q->next=New;
New->next=H;
return 0;
}
//链表打印
int list_show(linklist H){
if(NULL==H){
puts("empty\n");
return -1;
}
if(NULL==(H->next)){
puts("empty\n");
return -1;
}
linklist q=H;
while(q->next!=H){
printf("%d ",q->data);
q=q->next;
}
printf("%d ",q->data);
puts("");
return 0;
}
//链表删除 pos是删除的位置,sum是链表的节点个数
linklist list_delete(linklist H,int pos,int sum){
if(NULL==H){
puts("empty\n");
return NULL;
}
linklist p=(linklist)malloc(sizeof(linknode));
linklist q=(linklist)malloc(sizeof(linknode));
q=H;
for(int i=0;i<(pos+sum-1)%sum;i++){//此处完成指针的循环移动,移动到删除节点上一个位置
q=q->next;
}
if(NULL==q){
puts("empty\n");
return NULL;
}
if((q->next)==NULL){
printf("delete pos is invalid\n");
return NULL;
}
p=q->next;//完成删除
q->next=q->next->next;
//如果删除的节点为表尾,将删除后最后一个节点的next指向链表头
if(p->next==H){
q->next=H;
}
//如果删除位置是表头,将表头指向下一个节点
if(p==H){
H=p->next;
}
printf("free:%d\n",p->data);
free(p);
return H;
}
3.主函数
int main(int argc, char *argv[])
{
int sum=0;
printf("input sum\n");
scanf("%d",&sum);
linklist H=list_create(1);
for(int i =2;i<=sum;i++){
list_tail_insert(H,i);
}
list_show(H);
int kill;
printf("input kill\n");
scanf("%d",&kill);
int flag=1;//用来标志杀死的对象,并且移动指针
int step=0;//用来传递杀死的位置
while(H->next!=H){
linklist k=(linklist)malloc(sizeof(linknode));
k=H;
if(flag==kill){
H=list_delete(H,step,sum);//杀死目标
sum--;
flag=1;
}
if((step+1)==sum){ //位置置为0;
step=0;
}else{
step++;
}
flag++;
k=k->next;
list_show(H);
}
printf("survival is %d",H->data);
return 0;
}
运行结果

本文通过分析和示例介绍了约瑟夫环问题,利用单链表解决此问题,包括构建链表结构体、链表功能函数以及主函数的详细步骤,最后展示运行结果。
&spm=1001.2101.3001.5002&articleId=132282822&d=1&t=3&u=51aba3fe041c4ababb5e69af8998d652)
698

被折叠的 条评论
为什么被折叠?



