约瑟夫环问题介绍:
已知n个人(以编号1,2,3...n分别表示)围坐在一张圆桌周围。从编号为1的人开始报数,数到m的那个人出列;他的下一个人又从1开始报数,数到m的那个人又出列;依此规律重复下去,直到圆桌周围的人全部出列。
# include <stdio.h>
# include <stdlib.h>
# include <assert.h>
typedef int DataTp;
typedef struct node
{
DataTp elem;
struct node*pnext;
}NODE;
typedef NODE* LinkList;
NODE* CreatList(int n);
void MakeLoop(LinkList phead);
void DeleteNode(LinkList phead,LinkList paim);
int main (void)
{
int sum,num; //总人数 , 退出人的编号
int i;
LinkList pstart; //头节点
LinkList paim; //待删除的目标节点的地址
LinkList pt;
puts("输入总人数");
scanf("%d",&sum);
puts("输入退出的编号");
scanf("%d",&num);
pstart=CreatList(sum); //建立链表
MakeLoop(pstart); //变为循环链表
pt=pstart->pnext; //取出第一个数据节点地址
while(sum!=0)
{
for(i=0;i<num-1;i++)
pt=pt->pnext; //往后寻址,找到退出目标的地址
paim=pt; //临时保存。确定删除节点的地址
pt=pt->pnext; //确定下次循环的头为pt
printf("退出的是%d号\n",paim->elem);
DeleteNode(pstart,paim); //删除
sum--; //总人数减少
}
return 0;
}
NODE* CreatList(int n)
{
int i;
NODE*p1,*p2;
NODE * phead;
phead=(NODE*)malloc(sizeof(NODE));
if(phead==NULL)
{
fputs("memory error!\n",stderr);
exit(EXIT_FAILURE);
}
phead->pnext=NULL;
/
for(i=0;i<n;i++)
{
p1=(NODE*)malloc(sizeof(NODE)); //分配内存
if(p1==NULL) //检查合法性
{
fputs("memory error!\n",stderr);
exit(EXIT_FAILURE);
}
p1->elem=i+1; //补全结点信息
p1->pnext=NULL;
if(phead->pnext==NULL) //链接
phead->pnext=p1;
else
p2->pnext=p1;
p2=p1; //重置
}
return phead;
}
void MakeLoop(LinkList phead)
{
NODE*p=phead->pnext; //取出第一个数据节点
assert(p!=NULL);
while(p->pnext!=NULL)
p=p->pnext;
p->pnext=phead->pnext; // 链接,构成循环链表
return ;
}
void DeleteNode(LinkList phead,LinkList paim)
{
NODE*p=phead->pnext; //取出第一个数据节点
assert(p!=NULL);
while(p->pnext!=paim)
p=p->pnext;
p->pnext=p->pnext->pnext;
if(paim==phead->pnext) //如果删除的是第一个数据节点,
phead->pnext=p->pnext; //改变头节点指针域的值,使它始终指向
//循环链链表的某一个节点。而不丢失链表节点
free(paim);
return ;
}
链表初始状态:
刚开始写这个程序的时候,遇到一个Bug,那就是没有解决链表丢失问题。比如当退出的节点刚好为1号节点时
,1号节点就会被Free掉,这样就不能通过phead来访问各个节点了,整个链表就丢失了。
于是我在DeleteNode()函数里加了一个判断:当删除节点为第一个数据节点时,更改头节点的指针域的值,
使指向第一个数据节点的下一个节点,这样链表就不会丢失了。