线性表及其应用——约瑟夫环
约瑟夫(Joseph)问题的一种描述是:编号为1,2,3,…,n的n个人按顺时针方向围坐一圈。每人持有一个密码(正整数)。一开始任选一个正整数作为报数上限值m,从第一个人开始按顺时针方向自1开始顺序报数,报到m时停止报数,令其出列,将他的密码作为新的m值,从他在顺时针方向上的下一个人开始重新报数,如此下去,直到所有人全部出列为止。试设计一个程序求出出列顺序。
实验要求:利用无头结点的单向循环链表存储结构模拟此过程,按照出列的顺序打印出各人的编号。
测试数据:m的初值为20;n=7,7个人的密码依次为:3,1,7,2,4,8,4,首先出列的是6号(正确的出列顺序应为6,1,4,7,2,3,5)
实验提示:程序运行后,首先要求用户指定初始报数上限值,然后读取各人的密码,可设n<=30,此题所用的循环链表中不需要“头结点”,请注意空表和非空表的界限。
思路及题解:
这题可以用链表来做也可以用线性表来做,题目要求用链表那我们就用链表。
大致框架:
第一步 先创建一个结构体类型,里面存有一个人的序号和密码,并且还有个next节点。在输入n个人的密码时,每输入一个密码就创建一个节点,后插如链表中,并且next指向head(第一个节点时先创建head),这样就保证在输入的同时创建了循环链表。
第二步 用一个pre指针指向head,p指针指向head->next;cnt记录报数的人数,初值为2;当p非空时循环,在循环里,如果p==prev,则表示只剩下一个节点,输出p指向的序号后释放,然后跳出循环。当cnt==m时,p只想节点的序号输出,p后移,pre->next释放后,pre->next=p;cnt更新为1,其他情况下,p和pre后移,cnt+1。
代码:
#include<iostream>
using namespace std;
struct ListNode{
int num;
int val;
ListNode *next;
};
int main()
{
int m,n;
struct ListNode *head,*tail;
head=NULL;
cout<<"密码和人数为:";
cin>>m>>n;
cout<<n<<"个人的密码依次为:";
//创建没有头结点的循环链表
for(int i=1;i<=n;i++)
{
struct ListNode *s=new ListNode;
cin>>s->val;
s->num=i;
s->next=NULL;
if(i==1)
{
head=tail=s;
tail->next=s;
continue;
}
tail->next=s;
tail=tail->next;
tail->next=head;
}
struct ListNode *p,*pre;
//test
// p=head;
// int i=0;
// while(i<n)
// {
// i++;
// cout<<p->num<<" "<<p->val<<endl;
// p=p->next;
// }
// cout<<p->num<<" "<<p->val;
pre=head;
p=head->next;
int cnt=2;
while(p)
{
if(pre==p)
{//最后一个直接出队
cout<<p->num<<"号出队。 m="<<p->val<<endl;
delere p;
break;
}
if(cnt==m)
{
m=p->val;
cout<<p->num<<"号出队。 m="<<m<<endl;
cnt=1;
p=p->next;
delete pre->next;
pre->next=p;
}
else
{
pre=p;
p=p->next;
cnt++;
}
}
}