当先锋百科网

首页 1 2 3 4 5 6 7

约瑟夫环问题是经典的循环链表问题,具体描述如下:

题目:有n个人(编号分别为1,2,…,n)围成一圈,从编号为k的人开始顺时针报数,数到m的人出列;他的下一个人又从1开始,顺时针报数,数到m的人出列;依次重复下去,求剩下最后一人的编号。

假如有5个人(顺时针编号分别是1,2,3,4,5),编号3的人开始报数,数到2的人出列。则出列顺序依次为:

  1. 节点3 开始数1,然后节点4数2,所以节点4先出列;
  2. 节点4出列后,从节点5开始数1,节点1数2,所以节点1出列;
  3. 节点1出列后,从节点2开始数1,节点3数2,所以节点3出列;
  4. 节点3出列后,从节点5开始数1,节点2数2,所以节点2出列;
  5. 最后只剩下节点5

代码实现的思路:

  1. 创建循环链表,为每个节点申请内存并依次赋值为1,2,…,n
  2. 在链表中找到编号为k的节点,从k节点开始报数
  3. 报数为m的节点删除,然后下一个节点开始报数
  4. 直到链表中只剩下一个节点,输出该节点编号,判断链表中只有一个节点的方法是:某节点的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;
}

运行结果如下:
运行结果