约瑟夫环问题(单向循环链表实现)

问题描述

:编号为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();
}

运行结果如下:(当然数据可以自己输入)

在这里插入图片描述

猜你喜欢

转载自blog.csdn.net/qq_43753724/article/details/106422462