约瑟夫问题是个有名的问题:n个人围成一圈,从第一个开始报数,第m个将被杀掉,最后剩下一个,其余人都将被杀掉。例如n=6,m=5,被杀掉的顺序是:5,4,6,2,3,1。
我们知道第一个人(编号一定是(m-1)) 出列之后,剩下的n-1个人组成了一个新的约瑟夫环(以编号为k=m mod n的人开始)。通过循环结构进行报数,建立用于遍历的结点p,以及p的前驱结点prior,数到出列的人就把该结点从循环链表中删除并输出,直到循环链表为空(n个人都已经出列)为止,就可以求解这个问题。
第一步:建立单循环链表并输入n个人的数据;
第二步:在链表中确定开始报数的位置;
第三步:通过循环结构进行报数,建立用于遍历的结点p,以及p的前驱结点prior,数到出列的人就把该结点从循环链表中删除并输出,直到循环链表为空(n个人都已经出列)为止。
由于是在循环链表中搜索要出列的人,所以,采用带表尾指针且不带表头结点的单循环链表。
#include<iostream>
using namespace std;
//存储结构描述
typedef struct node
{
int data; //人员编号
node* next;//指向下一个结点的指针
}ListNode,*LinkList;
//创建带尾指针的单循环链表
void Create(LinkList &rear,int n)
{
ListNode *prior,*p;//创建p结点及其前驱结点prior
rear=prior=NULL;
//利用循环创建结点并存放入人员编号
for(int i=0;i<n;i++)
{
p=new ListNode;
p->data=i+1;
if(rear==NULL)//表尾指针先指向表首结点
rear=p;
if(prior!=NULL)
prior->next=p;
prior=p;
}
p->next=rear;
rear=p;
}
//约瑟夫循环
void Solve(LinkList &rear,int m,int s)
{
ListNode *prior,*p;
if(rear==NULL)
{
cout<<"It's empty!"<<endl;
return;
}
prior=rear;
p=rear->next;
for(int i=1;i<s;i++)
{
prior=p;
p=p->next;
}
//在链表中有大于一个人时进行循环
while(p->next!=p)
{
for(int j=0;j<m-1;j++)
{
prior=p;
p=p->next;
}
cout<<p->data<<" ";
prior->next=p->next;
delete p;
p=prior->next;
}
cout<<p->data;//输出最后一人的编号
delete p;
rear=NULL;
}
//主函数
int main()
{
LinkList rear;
int n,m,s;
cout<<"Please input n,m,s : "<<endl;
cin>>n>>m>>s;
Create(rear,n);
cout<<"The result is : "<<endl;
Solve(rear,m,s);
return 0;
}
运行结果: