【数据结构与算法】(C语言)——约瑟夫环

问题描述

约瑟夫问题的一种描述是:编号为1,2,...,n的n个人按顺时针方向围坐一圈,每人持有一个密码(正整数)。一开始任选一个正整数作为报数上限值m,从第一个人开始。按顺时针方向自1开始顺序报数,报到m时停止报数。报m的人出列,将他的密码作为新的m值,从他在顺时针方向上的下一个人开始重新从1报数,如此下去,直至所有人全部出列为止。试设计程序求出出列顺序。

基本要求

利用单向循环链表存储结构模拟此过程,按照出列的顺序印出个人的编号。

测试数据

m的初值为20;n=7,7个人的密码: 3,1,7,2,4,8,4。(正确的结果应为6,1,4,7,2,3,5)

(报告上要求写出多批数据测试结果

实现提示

程序运行后首先要求用户指定初始报数上限值与人数,然后读取各人的密码。

代码实现:

#include <stdio.h>
#include <stdlib.h>
#include <malloc.h>

#define LENGTH sizeof(struct member)		//宏定义结构体的长度 

typedef struct member
{
	int number;				//成员的初始位置 
	int secret;				//成员手里的密码 
	struct member *next;	//指向下一个成员 
}Member;

Member *Old_LinkList_creat(Member *old_head, int n);				//创建旧链表 
Member *LinkList_insert(Member *new_head, Member *p3, int n);		//将指定结点链接到新链表 
Member *New_LinkList_creat(Member *old_head, Member *new_head, int m, int n);//将所有满足要求的结点链接成新的链表
Member *LinkList_print(Member *new_head, int n);					//打印链表  
void DestroyList(Member *head, int n);								//释放链表 

int main()
{
	char ch = 'y'; 
	while(ch == 'y')
	{
		int m;								//第一次报数上限值 
		int n;								//成员人数 
		printf("输入第一次报数上限值m:");
		scanf("%d",&m);
		printf("输入成员的人数n:");
		scanf("%d",&n); 
		
		Member *old_head = (Member *)malloc(LENGTH);	//创建旧链表头结点 
		Member *new_head = (Member *)malloc(LENGTH); 	//创建新链表头结点 
		old_head->number = n;				//用来判断约瑟夫环是否结束的标志 
		new_head->next = NULL;				//用来判断插入新链表的结点是不是首元结点 
		
		old_head = Old_LinkList_creat(old_head, n);
		new_head = New_LinkList_creat(old_head, new_head, m, n);
		new_head = LinkList_print(new_head, n);
		DestroyList(new_head, n);
		
		printf("按y继续,按其它任意键结束:");
		getchar();
		scanf("%c",&ch);
		
		printf("\n");
		printf("~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~"); 
		printf("\n");
	}
	
	return 0; 
}

//创建旧链表
Member *Old_LinkList_creat(Member *old_head, int n)		
{		
	Member *p = old_head;	//LENGTH为宏定义的结构体长度 
	printf("依次输入%d位成员手里的密码(如:2 3):\n",n);
	int i = 0;
	while(i < n)
	{
		p->next = (Member *)malloc(LENGTH);
		scanf("%d",&p->next->secret);
		p->next->number = i + 1;
		p = p->next;
		i++;
	} 
	p->next = old_head->next;			//尾结点指向首元结点,形成循环链表 
	return old_head;
} 

//将指定结点链接到新链表尾结点 
Member *LinkList_insert(Member *new_head, Member *p3, int n)	 
{	
	Member *p1 = new_head->next;						
	if(new_head->next == NULL)
	{
		new_head->next = p3;
		p3->next = new_head->next;
	}
	else
	{
		while(p1->next != new_head->next)		//遍历找到尾结点 
		{
			p1 = p1->next;
		}
		p1->next = p3;
		p3->next = new_head->next;
	}
	return new_head;	
} 

//将所有满足要求的结点链接成新的链表  
Member *New_LinkList_creat(Member *old_head, Member *new_head, int m, int n)
{
	Member *p2, *p3;			 
	p2 = old_head;						//p2指向转移结点的直接前驱
	p3 = old_head->next;				//p3指向要转移的结点 
	int cnt = 0; 						//计数器,记录遍历每一个结点 
	
	while(old_head->number)
	{
		cnt++;							//先加1,判断p3是否为要转移的结点 
		if(cnt == m)
		{
			m = p3->secret;				//将目标结点手里的密码作为新的m值 
			p2->next = p3->next; 
			new_head = LinkList_insert(new_head, p3,n);
			cnt = 0;					//上一个目标结点结束,从新开始计数 
			old_head->number--;			//指示约瑟夫环(循环)结束的标志 
			p3 = p2->next;
		}
		else							//不是目标结点,p2、p3同步右移指向下一个结点 
		{
			p2 = p2->next;
			p3 = p3->next;
		}	
	}
	old_head->next = NULL;
	free(old_head);
	return new_head;
}

//打印链表 
Member *LinkList_print(Member *new_head, int n)
{
	printf("\n约瑟夫密码为:\n");
	Member *p4;
	p4 = new_head;
	for(int j = 1, i = 1; j <= n; j++, i++)
	{
		printf("%d\t",p4->next->number);
		p4 = p4->next;
		if(i%10 == 0)			//每10个数换行 
		{
			printf("\n");
		}
	}
	printf("\n");
	return new_head;
}

//释放链表 
void DestroyList(Member *head, int n)
{
	Member *p, *q;
	p = head;
	for(int i = 0; i <= n; i++)
	{
		q = p->next;						
		free(p);					//从头开始释放 
		p = q;
	}
}

 运行截图:

 

  • 1
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值