链接:环形链表的约瑟夫问题__牛客网
来源:牛客网
据说著名犹太历史学家 Josephus 有过以下故事:在罗马人占领乔塔帕特后,39 个犹太人与 Josephus 及他的朋友躲到一个洞中,39 个犹太人决定宁愿死也不要被敌人抓到,于是决定了一种自杀方式,41 个人排成一个圆圈,由第 1 个人开始报数,报数到 3 的人就自杀,然后再由下一个人重新报 1,报数到 3 的人再自杀,这样依次下去,直到剩下最后一个人时,那个人可以自由选择自己的命运。这就是著名的约瑟夫问题。现在请用单向环形链表得出最终存活的人的编号
解析:本题重点考察链表的创建与删除,需要编码者对链表的基本性质有较为深刻的理解
如图所示每次报数杀死以个人就会涉及到一次链表的删除问题,单向链表的删除除了要找删除对象还要找对象的前一个,在这里我们用两个指针分别实现。那最初要从链表的头结点报数,前一个指针放在尾结点处。
头结点尾结点依次向前,报到指定数删除cur指针指向的节点,未报到则每报一个数两个指针向前走一步,当cur节点自己指向自己时,cur指向节点的编码就是存活者的编号。
根据上述思路:先创建一个单向循环链表。
//定义节点
typedef struct SListNode {
int val;//存编码
struct SListNode* next;
} SLnode;
//创建节点
SLnode* Buynode(int i)
{
SLnode* node=( SLnode*)malloc(sizeof(SLnode));
if (node == NULL)
{
perror("malloc fail\n");
return NULL;
}
node->val = i;
node->next = NULL;
return node;
}
//连接节点形成链表
SLnode* CreateSlist(int n)
{
SLnode* phead = Buynode(1);
SLnode* ptail = phead;
for (int i = 2; i <= n; i++)
{
SLnode* tep = Buynode(i);
ptail->next = tep;
ptail = ptail->next;
ptail->val = i;
}
ptail->next = phead;
return ptail;
}
过程实现代码:
int main() {
int a, b;
while (scanf("%d %d", &a, &b) != EOF) { // 注意 while 处理多个 case
SLnode* ptail = CreateSlist(a);
SLnode* prev = ptail;
SLnode* phead = ptail->next;
SLnode* cur = phead;
while (cur->next != cur)
{
for (int i = 1; i < b; i++)//为什么从1开始,为什么是<b不是<=b.
{
cur = cur->next;//先前走
prev = prev->next;
}
prev->next = cur->next;//删除节点
free(cur);
cur = prev->next;
}
// 64 位输出请用 printf(\"%lld\") to
printf("%d\n", cur->val);
}
return 0;
}
注意控制报数与指针走的步数的关系。