已知n个人(以编号1,2,3...n分别表示)围坐在一张圆桌周围。从编号为k的人开始报数,数到m的那个人出列;他的下一个人又从1开始报数,数到m的那个人又出列;依此规律重复下去,直到圆桌周围的人全部出列。
这个就是约瑟夫环问题的实际场景,有一种是要通过输入n,m,k三个正整数,来求出列的序列。这个问题采用的是典型的循环链表的数据结构,就是将一个链表的尾元素指针指向队首元素。
输入n为-1时退出操作,代码如下:
#include<stdio.h>
#include<stdlib.h>
typedef struct node
{
int data;
struct node* next;
}Node;
Node *CreatLink(int n)
{//创建有n个结点的循环链表
int i;
Node *head=NULL; //p是原链表最后一个结点的地址
Node *p; //用p来保留q申请得到的地址
Node *q; //用q来不断申请空间存放编号的地址
p=(Node*)malloc(sizeof(Node));
head=p; //最开始P的地址就是头结点指向的地址
p->data=1; //开始存放第一个编号
for(i=2;i<=n;i++)
{
q=(Node*)malloc(sizeof(Node)); //q不断的申请内存地址空间
q->data=i; //为每一个结点存放一个数据编号
p->next=q; //p下一个指针指向q
p=q; //p=q即保留新的结点地址
}
p->next=head; //最后一个结点指向头部,形成循环链表
return head;
}
void PrintLink(Node *head,int k,int m)
{
int i;
Node *p=head;
Node *temp=p; //用temp临时保留创建的结点数
while(p->data!=k) //从第k个结点开始报数,让p指向的结点指向k
p=p->next;
if(m==1){
while(p->next!=p)
{
temp=p->next; //用temp指向p的下一个结点
printf("%d,",temp->data); //这时候temp用来开始报数
p->next=temp->next; //p的指针指向temp下一个结点
delete temp; //报到数的人退出
}
}else{
while(p->next!=p) //即直到链表存在最后一个结点时退出循环
{
for(i=1;i<m;i++)
{
temp=p;
p=p->next;
}
printf("%d,",p->data);
temp->next=p->next;
free(p); //被叫到号数结点出列释放内存空间
p=temp->next; //链表中存在两个及两个以上结点时继续从下一个结点开始报数
}
}
printf("%d\n",p->data);
free(p);
}
int main()
{
int n,m,k;
Node *head;
//printf("输入报数周期大于1且小于结点个数\n输入-1结束操作!\n");
printf("输入-1结束操作!\n");
printf("请输入结点个数,第几个结点开始,报数周期:\n");
while(scanf("%d",&n)&&n!=-1)
{
scanf("%d%d",&k,&m);
PrintLink(CreatLink(n),k,m); //m%n使得报道周期可以比结点个数大
printf("请输入结点个数,第几个结点开始,报数周期:\n");
}
printf("成功结束操作!\n");
return 0;
}
不对m的值是不是1进行判断,会使当k<=2时,出现bug