约瑟夫死者游戏-C语言实现-双向循环链表

这篇具有很好参考价值的文章主要介绍了约瑟夫死者游戏-C语言实现-双向循环链表。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

设计题目 1 约瑟夫生者死者游戏

[问题描述]:
约瑟夫生者死者游戏的大意是: 30 个旅客同乘一条船,因为严重超载,加上风高浪大,
危险万分;因此船长告诉乘客,只有将全船一半的旅客投入海中,其余人才能幸免遇难。无
奈,大家只得同意这种办法,并议定 30 个人围成一圈,由第一个人开始,依次报数,数到
9 人,便把他投入大海中,然后从他的下一个人数起,数到第 9 人,再将他投入大海,如
此循环,直到剩下 15 个乘客为止。问哪些位置是将被扔下大海的位置。
[设计要求]:
本游戏的数学建模如下:假设 n 个旅客排成一个环形,依次顺序编号 1 2 ,…, n 。从
某个指定的第 1 号开始,沿环计数,每数到第 m 个人就让其出列,且从下一个人开始重新
计数,继续进行下去。这个过程一直进行到剩下 k 个旅客为止。
本游戏的要求用户输入的内容包括:
(1) 旅客的个数,也就是 n 的值;
(2) 离开旅客的间隔数,也就是 m 的值;
(3) 所有旅客的序号作为一组数据要求存放在某种数据结构中。
本游戏要求输出的内容是包括: (1) 离开旅客的序号; (2) 剩余旅客的序号;
所以,根据上面的模型分析及输入输出参数分析,定义一种数据结构后进行算法实现。
设计题目 2 约瑟夫双向生死游戏
[问题描述]:
约瑟夫双向生死游戏是在约瑟夫生者死者游戏的基础上,正向计数后反向计数,然后再
正向计数。
具体描述如下: 30 个旅客同乘一条船,因为严重超载,加上风高浪大,危险万分;因此
船长告诉乘客,只有将全船一半的旅客投入海中,其余人才能幸免遇难。无奈,大家只得同
意这种办法,并议定 30 个人围成一圈,由第一个人开始,顺时针依次报数,数到第 9 人,
便把他投入大海中,然后从他的下一个人数起,逆时针数到第 5 人,将他投入大海,然后从
他逆时针的下一个人数起,顺时针数到第 9 人,再将他投入大海,如此循环,直到剩下 15
个乘客为止。问哪些位置是将被扔下大海的位置。
[设计思路]:
本游戏的数学建模如下:假设 n 个旅客排成一个环形,依次顺序编号 1 2 ,…, n 。从
某个指定的第 1 号开始,沿环计数,数到第 m 个人就让其出列,然后从第 m+1 个人反向计
数到 m-k+1 个人,让其出列,然后从 m-k 个人开始重新正向沿环计数,再数 m 个人后让其
出列,然后再反向数 k 个人后让其出列。这个过程一直进行到剩下 q 个旅客为止。
本游戏的要求用户输入的内容包括:
(1) 旅客的个数,也就是 n 的值;
(2) 正向离开旅客的间隔数,也就是 m 的值; (3) 反向离开旅客的间隔数,也就是 k 的值;
(4) 所有旅客的序号作为一组数据要求存放在某种数据结构中。

双向循环链表实现:

        链表采用带头结点的结构,便于链表的插入和判空。

        插入:直接利用头结点的前驱指针,可以找到链表的尾巴,然后呢通过头和尾进行插入。

        判空:令 头结点的后驱结点等于自身 为空。

        删除:需要注意将删除结点的前驱结点的后继指针和后继结点的前驱指针改掉

实现思路:

        由于链表带头结点,所以但头结点不包含信息,循环报数时(即以此遍历链表),会误将头结点作为玩家,因此头结点是一个冗余的结点,所以在游戏开始前,需要将头结点去掉,然后默认将编号为1的玩家当作头结点。游戏结束后指针一般不是指向编号最小的那个,所以可以通过前驱指针遍历,当当前结点的前驱结点的编号大于自身时循环结束,这样就找到编号最小的人。游戏结束后不要忘了将把链表改为有结点的链表,保证链表的完整性。文章来源地址https://www.toymoban.com/news/detail-771742.html

#include<stdio.h> 
#include<stdlib.h>
#include<assert.h>
#define ElemType int

// 双向循环链表实现
// 带头结点-不用于保存数据 ,用于判空即 头结点的前驱和后继相等 
typedef struct _Node{
	struct _Node* pre;// 前驱 
	ElemType elem;// 编号 
	struct _Node* next;// 后继 
} *List,Node;
// 创建一个空链表
List creatList(){
	List list = (List)malloc( sizeof(Node) );
	assert( list != NULL );
	list->pre = list->next = list;// 指向自身便于后续插入 
	return list;
}
// 尾插 
void push_back( List list, ElemType elem ) {
	assert( list );
	Node* node = (Node*)malloc( sizeof(Node) );
	assert( node );
	node->elem = elem; 
	
	node->next = list;// 新结点的后继是表头 
	node->pre = list->pre;// 新结点的后继是表头的前驱 
	list->pre->next = node;// 将老的尾部的next指向新结点 
	list->pre = node;// 将表头前驱指向新结点 
	
}
// 从头遍历一遍链表 
void printList( List list ){
	Node* start = list->next;
	while( start != list ){
		printf("%d ",start->elem);
		start = start->next;
	}
	printf("\n");
}
// 在传入结点所在的链表,将该结点删除 
int erase( Node* node ){
	assert( node != NULL );
	if( node->pre == node ){// 空链表,删除失败 
		return 0;
	}
 
	node->pre->next = node->next;// 改变前驱结点的后继 
	node->next->pre = node->pre;// 改变后继结点的前驱 
	free( node );
	return 1;
}

// 游戏,返回的链表保存了嘎的人的编号 
List game1( List* list, int n, int m, int k ){
	// 由于传入的链表是带有头结点的,多了一个空数据,不利于后续遍历
	// 所以改造成无头结点的 
	if( (*list)->pre == *list ) {// 无玩家,直接退出 
		return ;
	}
	Node* t = *list;// 保存头结点,便于后续恢复 
	// 改造为无头结点的链表 
	*list = (*list)->next;
	(*list)->pre = t->pre;
	t->pre->next = *list;
	free( t );
	List die = creatList();
	// 开始推人 
	int cnt = 1;
	while( n > k ){
		if( cnt == m ){
			t = (*list)->next;
			push_back( die, (*list)->elem );
			erase( *list );
			*list = t;
			cnt = 1;
			n--;
		} else {
			cnt++;
			*list = (*list)->next;
		}
	}
	// 恢复链表
	// 首先找到最小元素位置
	
	while( (*list)->pre->elem < (*list)->elem ){
		(*list) = (*list)->pre;
	}
//	printf("*****%d\n",(*list)->elem);
	// 将头结点插入
	t = (Node*)malloc(sizeof(Node));
	// 先让 头结点 主动与链表 逻辑链接 
	t->next = *list;
	t->pre = (*list)->pre;
	// 再让 链表 与头结点 逻辑链接 
	(*list)->pre->next = t;
	(*list)->pre = t;
	
	// 最后,链表指向头结点 
	*list = t;
	
	return die;
}

// 游戏,返回的链表保存了嘎的人的编号 
List game2( List* list, int n, int m, int k, int q ){
	// 由于传入的链表是带有头结点的,多了一个空数据,不利于后续遍历
	// 所以改造成无头结点的 
	if( (*list)->pre == *list ) {// 无玩家,直接退出 
		return ;
	}
	Node* t = *list;
	*list = (*list)->next;
	(*list)->pre = t->pre;
	t->pre->next = *list;
	free( t );
	List die = creatList();
	// 开始推人 
	int cnt = 1;
	int isSwitch = 1;//初始为顺时针 
	
	while( n > q ){	
		if( isSwitch ) {
			if( cnt == m ){
				t = (*list)->next;
				push_back( die, (*list)->elem );
				erase( *list );
				isSwitch = 0;
				cnt = 1;
				n--;
				*list = t;
			} else {
				cnt++;
				*list = (*list)->next;
			}
		} else {
			if( cnt == k ){
				t = (*list)->pre;
				push_back( die, (*list)->elem );
				erase( *list );
				isSwitch++;
				cnt = 1;
				n--;
				*list = t;
			} else {
				cnt++;
				*list = (*list)->pre;
			}
		}
		
	}
	
	while( (*list)->pre->elem < (*list)->elem ){
		(*list) = (*list)->pre;
	}
	// 将头结点插入
	t = (Node*)malloc(sizeof(Node));
	// 先让 头结点 主动与链表 逻辑链接 
	t->next = *list;
	t->pre = (*list)->pre;
	// 再让 链表 与头结点 逻辑链接 
	(*list)->pre->next = t;
	(*list)->pre = t;
	
	// 最后,链表指向头结点 
	*list = t;
		
	return die;
}

int getLength( List list ){
	Node* node = list->next;
	int cnt = 0;
	while( node != list ){
		cnt++;
		node = node->next;
	}
	return cnt;
}




int main(){
	
	int n,m,k,i,q;
	printf("第一个游戏的输入:(总人数,间隔,剩余人数)\n");
	scanf("%d %d %d",&n,&m,&k);
	List list1 = creatList();
	List list2 = creatList();
	
	for( i = 1; i <= n; i++ ){
		push_back( list1, i );
	}
//	printf("长度:%d\n",getLength(list1));
//	printf("长度:%d\n",getLength(list1));
	List die1 = game1( &list1, n, m, k );
	printf("题目一:\n");
	printf("剩余旅客编号:(%d人,按编号升序 ) ",getLength(list1));
	printList( list1 );
	printf("离开旅客编号:(%d人按先离开次序) ",getLength(die1));
	printList( die1 );
	printf("\n");
	
	printf("第二个游戏的输入:(总人数,顺时针间隔,逆时针间隔,剩余人数)\n");
	scanf("%d %d %d %d",&n,&m,&k,&q);
	for( i = 1; i <= n; i++ ){
		push_back( list2, i );
	}
	List die2 = game2( &list2, n, m, k, q );
	printf("题目二:\n");
	printf("剩余旅客编号:(%d人,按编号升序 ) ",getLength(list2));
	printList( list2 );
	printf("离开旅客编号:(%d人按先离开次序) ",getLength(die2));
	printList( die2 );
	
	
	return 0; 
}

到了这里,关于约瑟夫死者游戏-C语言实现-双向循环链表的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 303.【华为OD机试】报数游戏(约瑟夫环算法解题—Java&Python&C++&JS实现)

    本文收录于专栏:算法之翼 本专栏所有题目均包含优质解题思路,高质量解题代码(JavaPythonC++JS分别实现),详细代码讲解,助你深入学习,深度掌握!

    2024年04月15日
    浏览(24)
  • 神奇的约瑟夫环(C语言)

    约瑟夫环是一个古老而有趣的问题,它涉及人与人之间的生死较量,引发了人们长久以来的思考和探索。这个问题可以通过不同的方式来解决,每种方式都有其独特的优缺点。 使用数组实现约瑟夫环可以简单直观地表示人员的顺序,但受到数组大小静态限制和数据复制的操作

    2024年02月13日
    浏览(24)
  • C语言:约瑟夫环问题详解

    前言 哈喽,宝子们!本期为大家带来一道C语言循环链表的经典算法题(约瑟夫环)。 据说著名历史学家Josephus有过以下的故事:在罗马人占领乔塔帕特后,39个犹太人与Josephus及他的朋友躲到一个洞中,39个犹太人决定宁愿死也不要被人抓到,于是决定了一个自杀方式,41个人

    2024年04月13日
    浏览(23)
  • C语言 | 约瑟夫问题(猴王争夺战)

             约瑟夫问题有时也称为约瑟夫斯置换,是一个出现在计算机科学和数学中的问题。在计算机编程的算法中,类似问题又称为约瑟夫环。下面我们将用猴子争大王这一故事以及采用单向循环链表这一方法来进行讲解这一问题。         设编号为1,2,……n得n个猴子围

    2024年02月01日
    浏览(26)
  • 数据结构—约瑟夫环问题(C语言版)

    目录 首先什么是约瑟夫环 约瑟夫环实现方式 一、创建结构体变量 二、初始化链表 三、构建循环链表 四、删除链表  五、完整代码及注释讲解 约瑟夫环 是 循环链表 中的一个经典问题;题目描述:n 个人围成一圈,从第一个人开始报数,数到 m 的人出列,再由下一个人重新

    2024年02月11日
    浏览(32)
  • 约瑟夫环问题_数组解决_C语言

    有n个人围成一个圈,开始报数,报到m的人出局,并且不再参加报数,依次类推,直到n个人全部出局为止。 1.将抽象的问题实例化 假设有一个裁判: mouth:取值范围 1~m (报到m的人出局) finger: 取值范围1~n   (总共有n个人) 2.对n,m赋值,个人模拟报数 假设 n==8    , 

    2024年02月03日
    浏览(25)
  • 【c语言习题】使用链表解决约瑟夫问题

    创作不易,本篇文章如果帮助到了你,还请点赞 关注支持一下♡𖥦)!! 主页专栏有更多知识,如有疑问欢迎大家指正讨论,共同进步! 🔥c语言系列专栏:c语言之路重点知识整合 🔥 给大家跳段街舞感谢支持!ጿ ኈ ቼ ዽ ጿ ኈ ቼ ዽ ጿ ኈ ቼ ዽ ጿ ኈ ቼ ዽ ጿ ኈ ቼ 链表有

    2024年02月08日
    浏览(29)
  • C语言数据结构-用链表解决约瑟夫环问题

    只是普通的大学生一枚,不会很牛的技巧和算法,只是在做数据结构作业中的一点感悟和思考。也不知道自己写得对不对,有什么意见和建议都可以直接指出来哦,我虚心接受(低头鞠躬.jpg)...... 试用线性表的链表存储结构来实现约瑟夫(Josephu)问题。约瑟夫问题如下:设有

    2024年02月06日
    浏览(34)
  • 从古迷题到现代奇迹:神奇的约瑟夫环(C语言)

    约瑟夫环是一个古老而有趣的问题,它涉及人与人之间的生死较量,引发了人们长久以来的思考和探索。这个问题可以通过不同的方式来解决,每种方式都有其独特的优缺点。 使用数组实现约瑟夫环可以简单直观地表示人员的顺序,但受到数组大小静态限制和数据复制的操作

    2024年02月15日
    浏览(27)
  • 【算法】约瑟夫环问题解析与实现

    约瑟夫环(Josephus Problem)是一个经典的数学问题,涉及一个编号为 1 到 n 的人围成一圈,从第一个人开始报数,报到某个数字 m 的人出列,然后再从下一个人开始报数,如此循环,直到所有人都出列。本篇博客将详细解析约瑟夫环问题,并使用 Python 实现算法。 在约瑟夫环问

    2024年02月06日
    浏览(34)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包