数据结构//1、约瑟夫问题

提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档


前言

提示:数据结构与算法设计乐学题目解析

原创,用于自我总结


提示:以下是本篇文章正文内容,下面案例可供参考

一、约瑟夫问题?

题干:已知 n 个人(不妨分别以编号 1,2 ,3,…,n 代表 )围坐在一张圆桌周围,从编号为 k 的人开始,从1开始顺时针报数 1, 2, 3, ...,顺时针数到m 的那个人,出列并输出。然后从出列的下一个人开始,从1开始继续顺时针报数,数到 m的那个人,出列并输出,…依此重复下去,直到圆桌周围的人全部出列。

输入:n, k, m

输出:按照出列的顺序依次输出出列人的编号,编号中间相隔一个空格,每10个编号为一行。

非法输入的对应输出如下:

a)

输入:n、k、m任一个小于1
输出: n,m,k must bigger than 0.

b)

输入:k>n

输出: k should not bigger than n.

例:

输入:9,3,2

输出:4 6 8 1 3 7 2 9 5

二、题目分析 

1.题目概述:

n个人,从k个开数,数到m,出去一个人,直到全部出去。输出时注意数据之间有空格,且每十个换一行。

2.话不多说,上代码

代码如下(示例):

法一:第一个做法是用数组实现的,其中有些小细节需要注意:

#include <stdio.h>
int main()
{
	int n,k,m;//n人k号开始数到m
	int a[10000];
	int i,j=1;
	scanf("%d,%d,%d",&n,&k,&m);
	
	if(n<1||k<1||m<1)//特殊情况处理
		printf("n,m,k must bigger than 0.\n");
	else if(k>n)
		printf("k should not bigger than n.\n");
	
	else//一般情况
	{
		for(i=1;i<=n;i++)
		{
			if(i<=n-k+1)
				a[i]=k+i-1;
			else
			{
				a[i]=j;
				j++;
			}
		}
		
		int flag=1,count=0;
		int sum=n;
		while(sum)
		{
			for(i=1;i<=n;i++)
			{
				if(a[i]!=0)
					count++;
				if(count==m)
				{
					count=0;
					if(flag%10!=0&&sum!=1)
					printf("%d ",a[i]);
					else if(flag%10==0||sum==1)
					printf("%d\n",a[i]);
					flag++;
					a[i]=0;
					sum--;
				}
			}
		}
	}
}

①这种方法我利用for循环使得a[i]数组存储变为:从第k个数开始存储,然后再存1、2、3......这样就解决了从第k个数开始的问题。

②利用count==m来判断每到m出去一个人,注意每次数到非零数组count需要++,同时每到m个,count还需初始化为0。

③本题对输出有空格和回车的要求,因此利用flag%10是否等于零可以判断,是否满十个。但是需要注意不管是否满十个,若是最后一个出局的数字,也是需要无空格且有回车,需要单独控制。

④本题让我改bug的两个点:一是while循环条件要用n--,会导致下面再利用n判断,会发生改变,故用一个新的变量sum存储;另外出现了RE的情况,该法利用了数组的方法,猜测是数组越界,从a[100]改到了a[10000]终于解决。

法二:利用链表的方法更为简洁,但是我的链表学的不太好:

https://blog.csdn.net/qq_29303759/article/details/70260033

参考学习了上面这位大佬的代码!thx

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

#define ListSize 100  
typedef int DataType;  
typedef struct Node  
{  
	DataType data;  
	struct Node *next;  
}ListNode,*LinkList;  

/*函数声明*/  
LinkList CreateCycList(int n);/*创建一个长度为n的循环单链表的函数声明*/  
void Josephus(LinkList head,int n,int m,int k); /*在长度为n的循环单链表中,报数为编号为m的出列*/  
void DisplayCycList(LinkList head);/*输出循环单链表*/  

LinkList CreateCycList(int n)   /*宏定义和单链表类型定义*/  
{  
	LinkList head=NULL;  
	ListNode *s,*r;  
	int i;  
	for(i=1;i<=n;i++)  
	{  
		s=(ListNode*)malloc(sizeof(ListNode));  
		s->data=i;  
		s->next=NULL;  
		if(head==NULL)  
			head=s;  
		else    
			r->next=s;  
		r=s;  
	}  
	r->next=head;  
	return head; 
}  

void Josephus(LinkList head,int n,int m,int k)  /*在长度为n的循环单链表中,从第k个人开始报数,数到m的人出列*/  
{  
	ListNode *p,*q;  
	int i,j;  
	p=head;  
	for(i=1;i<k;i++)     /*从第k个人开始报数*/  
	{  
		q=p;  
		p=p->next;  
	}  
	for(j=1;j<=n;j++)  
	{  
		if (j==1)
			j=1;  
		else if((j-1)%10==0)
			printf("\n");  
		else 
			printf(" ");  
		for(i=1;i<m;i++) /*数到m的人出列*/  
		{  
			q=p;  
			p=p->next;  
		}  
		q->next=p->next;  /*将p指向的结点删除,即报数为m的人出列*/  
		printf("%d",p->data);  
		free(p);  
		p=q->next;           /*p指向下一个结点,重新开始报数*/  
	}  
	printf("\n",p->data);  
}  

void DisplayCycList(LinkList head)   /*输出循环链表的每一个元素*/  
{ ListNode *p;  
	p=head;  
	if(p==NULL)  
	{ 
		printf("该链表是空表");  
		return;  
	}  
	while(p->next!=head)   /*如果不是最后一个结点,输出该结点*/  
	{  
		printf("%4d",p->data);  
		p=p->next;  
	}  
	printf("%4d\n",p->data);/*输出最后一个结点*/  
}

int main()  
{  
	LinkList h;  
	int n,k,m;  
	scanf("%d,%d,%d",&n,&k,&m);  
	if(n<1||k<1||m<1)
		printf("n,m,k must bigger than 0.\n");  
	else if(k>n)
		printf("k should not bigger than n.\n");  
	else  
	{  
		h=CreateCycList(n);  
		Josephus(h,n,m,k);  
	}  
	system("pause");  
	return 0;             
}  

总结

本题难度不大,在操作上有些小细节需要不断调试。第一篇正式题解完结撒花

*★,°*:.☆( ̄▽ ̄)/$:*.°★* 。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值