约瑟夫环问题是经典的循环链表
问题,具体描述如下:
题目:有n个人(编号分别为1,2,…,n)围成一圈,从编号为k的人开始顺时针报数,数到m的人出列;他的下一个人又从1开始,顺时针报数,数到m的人出列;依次重复下去,求剩下最后一人的编号。
假如有5个人(顺时针编号分别是1,2,3,4,5),编号3的人开始报数,数到2的人出列。则出列顺序依次为:
- 节点3 开始数1,然后节点4数2,所以节点4先出列;
- 节点4出列后,从节点5开始数1,节点1数2,所以节点1出列;
- 节点1出列后,从节点2开始数1,节点3数2,所以节点3出列;
- 节点3出列后,从节点5开始数1,节点2数2,所以节点2出列;
- 最后只剩下节点5
代码实现的思路:
- 创建循环链表,为每个节点申请内存并依次赋值为1,2,…,n
- 在链表中找到编号为k的节点,从k节点开始报数
- 报数为m的节点删除,然后下一个节点开始报数
- 直到链表中只剩下一个节点,输出该节点编号,判断链表中只有一个节点的方法是:某节点的next指针指向自身
#include <stdio.h>
#include <stdlib.h>
// 链表节点的定义
struct ListNode {
int val;
struct ListNode *next;
};
// 创建链表,为每个节点申请内存,并赋值
struct ListNode *CreateList(int n) {
struct ListNode *head = (struct ListNode *)malloc(sizeof(struct ListNode));
struct ListNode *tmp = head;
struct ListNode *node = NULL;
head->val = 1;
head->next = NULL;
for (int i = 1; i < n; i++) {
node = (struct ListNode *)malloc(sizeof(struct ListNode));
node->val = i + 1;
node->next = NULL;
tmp->next = node;
tmp = tmp->next;
}
尾节点的next指向头节点,实现循环链表
tmp->next = head;
return head;
}
void KillK(struct ListNode *head,int k,int m) {
struct ListNode *pre = head;
struct ListNode *nodeK = head;
// 找到编号为k的人
while (nodeK->val != k) {
pre = nodeK;
nodeK = nodeK->next;
}
// 循环退出条件:列表中只有自己这个节点
while (nodeK->next != nodeK) {
// k开始报数,查找报m的人及前一个人的位置pre
for (int i = 1; i < m; i++) {
pre = nodeK;
nodeK = nodeK->next;
}
pre->next = nodeK->next;
printf("出列人的编号为:%d\n", nodeK->val);
free(nodeK);
nodeK = pre->next;
}
printf("出列人的编号为:%d\n", nodeK->val);
free(nodeK);
}
int main()
{
int totalNum = 5; // 总人数
struct ListNode *head = CreateList(totalNum);
int k = 3; // 从第k人开始报数
// 数到m的人出列
int m = 2;
KillK(head, k, m);
return 0;
}
运行结果如下: