数据结构与算法之约瑟夫环。
与其枯燥的讲解约瑟夫环,倒不如用约瑟夫环来解决一个有趣的问题。
猴子选大王问题:
现在有N个猴子需要选取一个猴王,这N个猴子手拉手围成一个圈,旁边有一位德高望重的老猴,先将这一圈猴按顺时针方向编号1,2.......N,然后给每只小猴一张纸条,上面记录着一个数字m(非负数),并且由老猴王随机报出一个数num,该数为初始数字,由编号为1号的猴开始顺时针报数,如果那个猴报出了这个num那么将该猴踢出这个环,并且以该猴持有的纸条上的数为num,由该猴的下一位猴开始从1报数,以此类推,最后只剩一只猴子便为猴王
解题步骤
1.创建保存每一只猴子序号,和持有纸条上数字的结构体。
//创建一个结构体,记录猴子的编号和持有的纸条
typedef struct monkey
{
int num;//编号
int secret;//持有的纸条
struct monkey *next;
}Monk;
2.将猴子存进环状单项链表中。
printf("请输入猴子的个数:");
scanf("%d",&monknum);
for(int i=0;i<monknum;i++)
{
pnew=(Monk*)malloc(sizeof(Monk));
printf("请输入猴子的编号:");
scanf("%d",&pnew->num);
printf("\n");
printf("请输入猴子的密码:");
scanf("%d",&pnew->secret);
printf("\n");
//如果没有头节点,则将pnew赋值给phead(头节点)
if(phead==NULL)
{
phead=pnew;
pend=pnew;
pnew->next=phead; //注意,我们将phead赋值给pnew->next,
//这样该链表就由一个单向链表变成一个环状链表了
}
//如果头节点已经存在
else
{
pend->next=pnew;
pend=pnew;
pnew->next=phead;//注意,我们将phead赋值给pnew->next,
//这样该链表就由一个单向链表变成一个环状链表了
}
}
3.输入初始密码(num),开始踢出猴子。
printf("请输入起始的密码敲击回车后将选出猴王我们默认由一号猴子开始展示初始密码后开始报数:");
scanf("%d",&secretsta);
printf("\n");
Monk *flag=phead;
Monk *before=NULL;//保存whlie循环中当前节点的上一个节点;
while(flag->next!=flag)//如果flag->next==flag则表明只剩下一个节点了,这个节点保存的num就是猴王的num;
{
secretsta--;
if(secretsta==0)//当secretsta减到0就踢出这个猴子
{
before->next=flag->next;
secretsta=flag->secret;//将该猴子所持有的纸条上的数字设置为新得secretsta;
free(flag);//释放该节点的资源
}
before=flag;
flag=flag->next;
}
printf("猴王是%d号猴子!\n",flag->num);
这样就完成了。
心得:本题用来锻炼对链表的掌握再好不过了,本题的难点有二,第一,创建环状链表。第二,当一只猴子被踢出后如何衔接上继续执行循环。