数据结构循环链表解决约瑟夫问题(C语言)

首先了解约瑟夫问题:

 据说著名犹太历史学家Josephus有过以下的故事:在罗马人占领乔塔帕特后,39 个犹太人与Josephus及他的朋友躲到一个洞中,39个犹太人决定宁愿死也不要被敌人抓到,于是决定了一个自杀方式,41个人排成一个圆圈,由第1个人开始报数,每报数到第3人该人就必须自杀,然后再由下一个重新报数,直到所有人都自杀身亡为止。然而Josephus 和他的朋友并不想遵从。首先从一个人开始,越过k-2个人(因为第一个人已经被越过),并杀掉第k个人。接着,再越过k-1个人,并杀掉第k个人。这个过程沿着圆圈一直进行,直到最终只剩下一个人留下,这个人就可以继续活着。问题是,给定了和,一开始要站在什么地方才能避免被处决。Josephus要他的朋友先假装遵从,他将朋友与自己安排在第16个与第31个位置,于是逃过了这场死亡游戏。 

在数据结构里,用循环链表就可以很好的实现这个问题,效果如图:

该次程序写的比较简单,只能实现逻辑内的运算,没有对输入值进行错误判定,

目录

//主函数

//菜单

//约瑟夫问题

//求猴子大王

 总结:


//主函数

#include<stdio.h>

typedef int ElemType;
typedef struct Lnode {
	ElemType data;
	struct Lnode* next;
}Lnode,*L;

Lnode Link;//创建链表
L head=&Link;//头指针


int main(){
	system("title 循环链表操作");
	int n;
	while (1) {
		menu();	//菜单
		scanf("%d", &n);	//输入选择
		switch (n) {
		case 1:system("cls"); system("title 约瑟夫问题"); YSF(); break;	//约瑟夫问题
		case 2:system("cls"); system("title 求猴子大王"); MENOKEYKING(); break;	//猴子大王
		case 0:free(Link); return;	//退出
		default:printf("输入错误!\n");
		}
	}
	return 0;
}

//菜单

void menu() {
	printf("+-+-+-+-+-+-+-+-+-+-+-+\n");
	printf("+-   循环链表的用法  -+\n");
	printf("+---------------------+\n");
	printf("+-   1、约瑟夫问题   -+\n");
	printf("+-   2、猴子取大王   -+\n");
	printf("+-      0、退出      -+\n");
	printf("+---------------------+\n");
	printf("+-+-+-+-+-+-+-+-+-+-+-+\n");
	printf("请选择:\n");
}

//约瑟夫问题


//约瑟夫创建链表函数
void Create() {
	head->next = NULL;	//将头指针指向NULL
	L h = head;	//代替头指针,用来移动
	printf("请先初始化:(-1结束)\n");
	while (1) {
		L new = (L)malloc(sizeof(L));	//新建结点
		scanf("%d", &new->data);	//赋值
		getchar();	//用来接受掉多余的 空格
		if (new->data == -1) {	//-1结束
			return;
		}
		h->next = new;	//将新节点连上
		new->next = head->next;	//直接将新节点的next指向第一个结点
		h = h->next;	//h后移
	}
}
//约瑟夫问题
void YSF() {
	Create();	//初始化链表
	printf("请输入起点结点的数据:\n");
	int begin;	
	scanf("%d", &begin);
	printf("请输入目标大小:\n");
	int num;
	scanf("%d", &num);
	L h = head->next;	//代替头指针用来移动
	L f = head;	//作h的前一个结点,用来找到h
	//先用循环找到起点
	while (1) {
		if (h->data == begin) {
			break;
		}
		f = f->next;
		h = h->next;
	}
	
	int count = 1;	//计数
	while (h->next!=h) {	//只剩下一个结点的条件
		h = h->next;	
		f = f->next;
		count++;	//移动一个结点就加一
		if (count == num) {	//加到目标值了
			f->next = h->next;	//将该节点去除
			h = f->next;	//h后移
			count = 1;	//重置count
		}
	}
	printf("最后一个结点是:%d\n", h->data);
}

//求猴子大王

//猴子大王
void MENOKEYKING() {
	printf("请输入猴子的个数:\n");
	int num;
	scanf("%d", &num);
	L h = head;	//代替头指针用来移动
	for (int i = 1; i <= num; i++) {	//循环为猴子们编号,和上面差不多
		L new = (L)malloc(sizeof(L));
		new->data = i;
		h->next = new;
		new->next = head->next;
		h = new;
	}
	printf("你想从第几只猴子开始:\n");
	int n;	//起点
	scanf("%d", &n);
	h = head->next;	//将h移回第一个结点的位置,方便下面的f
	L f = head;	//作h的前一节点,如果h不是第一个结点的话,那么f就不好定义
	while (1) {	//循环找到起点
		h = h->next;
		f = f->next;
		if (h->data == n) {
			break;
		}
	}
	printf("请输入目标数:\n");	
	int a;
	scanf("%d", &a);

	int count = 1;	//计数
	while (h != h->next) {	//只剩一个结点的条件,下与上同
		h = h->next;
		f = f->next;
		count++;
		if (count == a) {
			f->next = h->next;
			h = f->next;
			count = 1;
		}
	}
	printf("猴子大王是:%d号猴子!\n", h->data);
}

 总结:

1.循环链表一般用到尾指针,虽然说我没有用到:

 1.1 尾指针可以在第一个结点插入的时候就设置,然后可以头插法保持尾指针位置不变,等结点插完就直接指向第一个结点,构成循环;

 1.2 也可以在第一个结点插入时设置,然后尾插法,只是要记得将尾指针向后移动;

 1.3 或者可以等到单链表初始化完成,进行循环,指定尾指针,然后再构成循环。

2.我这里构成循环是直接用新插入的结点指向第一个节点,是用的尾插法插入结点

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

多低调

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值