提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档
文章目录
目录
前言
提示:数据结构与算法设计乐学题目解析
原创,用于自我总结
提示:以下是本篇文章正文内容,下面案例可供参考
一、约瑟夫问题?
题干:已知 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;
}
总结
本题难度不大,在操作上有些小细节需要不断调试。第一篇正式题解完结撒花
*★,°*:.☆( ̄▽ ̄)/$:*.°★* 。