已知n个人(以编号1,2,3,…,n分别表示)围坐在一张圆桌周围。从编号为k的人开始报数,数到m的那个人出列;他的下一个人又从K开始报数,数到m的那个人又出列;依次规律重复下去,直到圆桌周围的人全部出列。
解:本题是约瑟夫环问题的实际场景。可用循环链表来实现。问题的核心步骤为: 建立无头结点的循环链表; 确定第一个报数人的位置; 不断从链表中删除结点,直到链表为空。
代码如下:
#include<stdio.h>
#include<stdlib.h>
//不带头结点的循环链表 //约瑟夫环问题
typedef struct LNode
{
int data;
struct LNode* next;
}LNode,*LinkList;
void JOSEPHUS (int n,int k,int m)//n人数,k第一个开始报数的人,m出列者喊到的数
{
LinkList p,head;
//建立循环链表
head=(LinkList)malloc(sizeof(LNode));
head->data=1;
head->next=NULL;
p=head;
for(int i=2;i<=n;++i)
{
LinkList s=(LinkList)malloc(sizeof(LNode));
s->data=i;
s->next=NULL;
p->next=s;
p=p->next;
}
p->next=head;
//找到尾结点,也就是第一个结点的上一个结点
LinkList tail=head;
while(tail->next!=head)
{
tail=tail->next;
}
//找编号为k的人
p=head;
while(p->data!=k)
{
tail=p;
p=p->next;
}
//只有当p->next==p时,说明链表中只剩p结点了
while(p->next!=p)
{
//找到从p开始报数,报m的人,tail始终为p的上一个结点,为删除p做准备
for(int i=1;i<m;++i)
{
tail=p;//tail跟上p,tail为p的前驱结点
p=p->next;
}
tail->next=p->next;
printf("出列人编号:%d\n",p->data);
free(p);//删除p
p=tail->next;//p指向删除后的下一个结点,重新进入循环
}
//只剩p一个结点
printf("出列人编号:%d\n",p->data);
free(p);
}
int main()
{
JOSEPHUS(5,3,2);//测试用例
return 0;
}
运行结果如下: