用链表解决约瑟夫问题

文章介绍了如何运用单向循环链表解决约瑟夫问题,展示了创建链表、初始化节点、模拟报数淘汰过程的步骤,最终得出猴王的编号。代码实现简洁明了,适合新手学习。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

首先我们要知道什么是约瑟夫问题?

       设编号为1,2,……n得n个人围坐一圈,约定编号为k(k大于等于1并且小于等于n)的人从1开始报数,数到m的那个人出列。它的下一位继续从1开始报数,数到m的人出列,依次类推,最后剩下一个为就为猴王。

        初始化状态:假设n=6,总共有6个人,k=1,从第一个人开始报数,m=5,每次数五个。第一次报数:从一号开始,数五个数,1-2-3-4-5,数完五个数,五号被杀死,第一次报数后,剩余人数为5。

        第二次报数:从被杀死的五号的下一位开始报数,也就是六号,数五个数,6-1-2-3-4,数数完毕,四号被杀死。

        第三次报数:从被杀死的四号的下一位开始报数,同样是六号,数五个数,6-1-2-3-6,数数完毕,六号被杀死。

        第四次报数:从被杀死的六号的下一位开始报数,也就是一号,数五个数,1-2-3-1-2,数数完毕,二号被杀死。

        第五次报数:从被杀死的二号的下一位开始报数,也就是三号,数五个数,3-1-3-1-3,数数完毕,三号被杀死,只剩下一号。

        以上是假设有6人时选出最后的猴王的步骤,我看到其他博主用的链表的方法较为复杂,作为一个新手,我在这里展示一下我的拙见,如有不足,敬请斧正。

1.进行主函数和结构体的定义

#include <stdio.h>
#include <stdlib.h>
typedef struct node_t
{
	int data;
	struct node_t *next;
}link_node_t,*link_list_t;

 2.在主函数中定义运行所需要的变量(int也可以用scanf进行替换)

int main(int argc, const char *argv[])
{
	int i;
	link_list_t pdel = NULL;//用于指向被删除节点
	link_list_t ptail = NULL;//永远指向当前链表的尾 
	link_list_t pnew = NULL;//永远指向新创建的节点
	link_list_t h = NULL;
	int all_num = 6;//猴子总数 
	int start_num = 1; //从几号猴子开始数
	int kill_num = 5;//数到几杀死猴

3.接下来创建出一个单向循环链表
    (1)首先创建有all_num个节点的单向链表

h = (link_list_t)malloc(sizeof(link_node_t));
	if(NULL == h)
	{
		perror("malloc failed");
		return -1;
	}
	h->data = 1;
	h->next = NULL;
	ptail = h;//尾指针指向当前的第一个节点

(2)创建新的节点,并将新节点装上数据

for(i = 2; i <= all_num; i++)
	{
		pnew = (link_list_t)malloc(sizeof(link_node_t));
		if(NULL == pnew)
		{
			perror("malloc failed");
			return -1;
		}
	

(3)链接到链表的尾并将尾指针继续指向当前链表的尾 。

ptail->next = pnew;
		ptail = pnew;

(4)将头指针保存到链表的尾形成单向循环链表
                   形成单向循环链表 

ptail->next = h;

(5)开始杀猴子

        将头指针移动到开始猴子的号码处 
 

for(i = 0; i < start_num-1; i++)
		h = h->next;

(6)循环进行杀猴子

while(h != h->next)//条件不成的时候,就剩一个猴子,只有一个节点
	{
		//将头指针移动到即将删除节点的前一个节点
		for(i = 0; i < kill_num-2; i++)
			h = h->next;

		pdel = h->next;
		//跨过删除节点
		h->next = pdel->next;
		printf("kill is -------------%d\n",pdel->data);
		free(pdel);
		pdel = NULL;
		//杀死猴子猴,从下一个节点开始继续开始数,将头指针移动到开始数的地方
		h = h->next;
	}
	printf("king is %d\n",h->data);
	return 0;
}	

(7)最后打印出的就是约瑟夫问题的答案

下面是整个程序的完整代码

#include <stdio.h>
#include <stdlib.h>
typedef struct node_t
{
	int data;
	struct node_t *next;
}link_node_t,*link_list_t;

int main(int argc, const char *argv[])
{
	int i;
	link_list_t pdel = NULL;
	link_list_t ptail = NULL;
	link_list_t pnew = NULL;
	link_list_t h = NULL;
	int all_num = 6;
	int start_num = 1; 
	int kill_num = 5;
	h = (link_list_t)malloc(sizeof(link_node_t));
	if(NULL == h)
	{
		perror("malloc failed");
		return -1;
	}
	h->data = 1;
	h->next = NULL;
	ptail = h;
	for(i = 2; i <= all_num; i++)
	{
		pnew = (link_list_t)malloc(sizeof(link_node_t));
		if(NULL == pnew)
		{
			perror("malloc failed");
			return -1;
		}
		pnew->data = i;
		pnew->next = NULL;
		ptail->next = pnew;
		ptail = pnew;
	}
	ptail->next = h;
	for(i = 0; i < start_num-1; i++)
		h = h->next;
	while(h != h->next)
	{
		for(i = 0; i < kill_num-2; i++)
			h = h->next;

		pdel = h->next;
		h->next = pdel->next;
		printf("kill is -------------%d\n",pdel->data);
		free(pdel);
		pdel = NULL;
		h = h->next;
	}
	printf("king is %d\n",h->data);
	return 0;
}	

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值