有n个人围成一圈,从第1个人开始报数1、2、3,每报到3的人退出圈子。编程使用链表找出最后留下的人。
整体思路:创建一个链表, 结点里用成员保存每个人对应的号数,把尾结点和头结点连接起来构成循环链表,头结点隔一个结点删除一个结点,之后每隔两个结点删除一个结点,直到只剩下两个结点,输出两个结点中的后一个结点
bug版本
#include <stdio.h>
#include <stdlib.h>
struct player
{
long num;
struct player *next;
};
typedef struct player NODE;
NODE *create(int n)
{
NODE *head, *tail, *p;
int i = 0;
head = NULL;
while (++i <= n)
{
p = (NODE *) malloc (sizeof (NODE) );
p->num = i;//用i的值作为每个成员的号码
p->next = NULL;
if(head == NULL)
{
head = p;
}
else
{
tail->next = p;
}
tail = p;
//i++;
}
tail->next = head;
return head;
}
void *delnext(NODE *pre)
{
NODE *p;
p = pre->next;
pre->next = p->next;
free(p);
}
int main()
{
NODE *head, *pre, *p;
int n;
scanf("%d", &n);
head = create( n );
pre = head;
while(pre->next != NULL)
{
p = pre;
if(pre == head)//这里最大的bug就在于循环链表转一圈之后pre可能会再次等于head,所以在转了一圈之后还可能再次执行这个语句块
{
pre = pre->next;
delnext( pre );
}
else if(pre != head)
{
pre = pre->next;
if(pre->next == p)
{
pre = pre->next;
printf("%d", pre->num);
exit(0);
}
else if(pre->next != p)
{
pre = pre->next;
delnext( pre );
}
}
}
}
正确做法
#include <stdio.h>
#include <stdlib.h>
struct player
{
long num;
struct player *next;
};
typedef struct player NODE;
NODE *create(int n)
{
NODE *head, *tail, *p;
int i = 0;
head = NULL;
while (++i <= n)//这里的条件也可以用(i++ < n),think think why。简单点的写法就是把i初始化为1,然后条件(i<=n),在循环末尾加i++
{
p = (NODE *) malloc (sizeof (NODE) );
p->num = i;
p->next = NULL;
if(head == NULL)
{
head = p;
}
else
{
tail->next = p;
}
tail = p;
}
tail->next = head;//头结点连到尾结点后面
return head;
}
void *delnext(NODE *pre)//通过前驱结点删除要删除的结点
{
NODE *p;
p = pre->next;
pre->next = p->next;
free(p);
}
int main()
{
NODE *head, *pre, *p;
int n;
scanf("%d", &n);
head = create( n );
pre = head;
pre = pre->next;
delnext( pre );//直接删除第三个结点
while(pre->next != NULL)//循环里每隔两个结点删除一个
{
p = pre;
if(pre->next != p)//如果pre->next != p,说明还不只两个结点
{
pre = pre->next;
pre = pre->next;
delnext( pre );
}
else //如果pre->next == p,说明只剩下两个结点,p->num就是结果
{
printf("%d", p->num);
exit(0);
}
}
}