约瑟夫环问题(单向循环链表实现)
问题描述
:编号为1,2…n的n个人按顺时针方向围坐在一张圆桌周围,没人持有一个密码(正整数)。一开始人选一个正整数作为报数上线值m,从第一个人开始按顺时针方向自1报数,报到m时停止报数,报m的那个人出列,将他的密码作为新的m值,从他顺时针方向的下一个人开始重新从1报数,数到m的那个人又出列;如此下去,直至圆桌周围的人全部出列为止。这个游戏的实现只需将每个人的信息作为一个结点,节点中存放每个人的编号和密码,由于要反复做删除操作,所以采用单项循环链表实现较为方便。
算法分析:
采用单向循环链表的数据结构,即将链表的尾元素指针指向链首元素。每个结点除了指针域外,还有两个分别存放每个人的编号和所持有的密码。
解决问题的基本步骤如下:
1.建立n个结点(无头结点)的单向循环链表
2.从链表第一个结点开始循环计数寻找第m个结点。
3.输出该结点的id值,将该节点的password作为新的m值,,删除该结点。
4.根据m值不断从链表中删除结点,知道链表为空。
编程实现:
#include<stdio.h>
#include<stdlib.h>
#define MAX 100
typedef struct NodeType
{
int id; //编号
int password; //密码
struct NodeType *next; //用于指向下一个结点的指针
}NodeType;
void CreaList(NodeType **,int);//创建单向循环链表
NodeType *GetNode(int,int);//得到一个结点
void PrntList(NodeType *);//打印循环链表
int isEmptyList(NodeType *);//测试链表是否为空
void JosephusOperate(NodeType **,int);//运行约瑟夫环问题
int main(void)
{
int n=0;
int m=0;
NodeType *pHead=NULL;
do{
if(n>MAX)
{//人数n超过最大人数循环,接着做下一次循环,重新输入人数n,知道满足条件为止
printf("人数太多,请重新输入!\n");
}
printf("请输入人数n(最多%d个): ",MAX);
scanf("%d",&n);
}while(n>MAX);
printf("请输入初始密码m: ");
scanf("%d",&m);
CreaList(&pHead,n); //创建单向循环链表
printf("\n-----------------打印循环链表---------------\n");
PrntList(pHead); //打印循环链表
printf("\n-----------------打印出队情况---------------\n");
JosephusOperate(&pHead,m); //运行约瑟夫环问题
return 1;
}
void CreaList(NodeType **ppHead,int n)//创建有n个结点的循环链表ppHead
{
int i=0;
int iPassword=0;
NodeType *pNew=NULL;
NodeType *pCur=NULL;
for(i=1;i<=n;i++)
{
printf("输入第%d个人的密码: ",i);
scanf("%d",&iPassword);
pNew=GetNode(i,iPassword);
if(*ppHead==NULL)
{
*ppHead=pCur=pNew;
pCur->next=*ppHead;
}else{
pNew->next=pCur->next;
pCur->next=pNew;
pCur=pNew;
}
}
printf("完成单向循环链表的创建!\n");
}
NodeType *GetNode(int iId,int iPassword)//向结点中传送编号和密码
{
NodeType *pNew=NULL;
pNew=(NodeType *)malloc(sizeof(NodeType));//为当前结点开辟新空间
if(!pNew)
{
printf("Error,the memory is not enough!\n");
exit(-1);
}
pNew->id=iId;
pNew->password=iPassword;
pNew->next=NULL; //pNew的next指向空,置空表尾
return pNew;
}
int IsEmptyList(NodeType *pHead)
{
if(!pHead)
{
printf("the List is Empty!\n");
return 1;
}
return 0;
}
void PrntList(NodeType *pHead)//依次输出至n个人,且输出密码,完成原始链表的打印
{
NodeType *pCur=pHead;
if(!IsEmptyList(pHead))//调用EmptyList()函数来判断if语句是否执行,若pHead为空则执行
{
printf("--ID-- --PASSWORD---\n");
do
{
printf("%3d %7d\n",pCur->id,pCur->password);
pCur=pCur->next;
}while(pCur!=pHead);
}
}
void JosephusOperate(NodeType **ppHead,int iPassword)
{
int iCounter=0;
int iFlag=1;
NodeType *pPrv=NULL;
NodeType *pCur=NULL;
NodeType *pDel=NULL;
pPrv=pCur=*ppHead;
while(pPrv->next!=*ppHead)//将pPrv指向表尾结点,为删除做好准备
pPrv=pPrv->next;
while(iFlag)
{
for(iCounter=1;iCounter<iPassword;iCounter++)
{
pPrv=pCur;
pCur=pCur->next;
}
if(pPrv==pCur)
iFlag=0;
pDel=pCur; //删除pCur指向的结点,即有人出队列
pPrv->next=pCur->next;//使得pPrv指向结点与下下一个结点相连,让pCur从链表中脱节
pCur=pCur->next;//让指针pCur改为指向后继结点,后移一个结点
iPassword=pDel->password; //记录出列的人手中的密码
printf("第%d个人出列(密码:%d)\n",pDel->id,pDel->password);
free(pDel); //释放删除pDel指向的结点
}
*ppHead=NULL;
getchar();
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· Linux系列:如何用 C#调用 C方法造成内存泄露
· AI与.NET技术实操系列(二):开始使用ML.NET
· 记一次.NET内存居高不下排查解决与启示
· 探究高空视频全景AR技术的实现原理
· 理解Rust引用及其生命周期标识(上)
· DeepSeek 开源周回顾「GitHub 热点速览」
· 物流快递公司核心技术能力-地址解析分单基础技术分享
· .NET 10首个预览版发布:重大改进与新特性概览!
· AI与.NET技术实操系列(二):开始使用ML.NET
· 单线程的Redis速度为什么快?