数据结构与算法设计.绪论.程序题
属于百丽宫菜鸡的数据结构学习记录,欢迎大家指正与提出建议。
题干.约瑟夫问题
约瑟夫问题是一个经典的问题。已知 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
个人思考
- 开一个数组用来填空(标记),并实时检测
- 环状链表
代码更迭
想法一
#include<stdio.h>
#include<string.h>
#define N 1000
int que[N];
int out[N];
int check(int n,int que[]){
for(int i = 1;i <= n;i++){
if(que[i] == 1)return 0;
}
return 1;
}//检查是否每一个数字都被标记
void solve(int n,int k, int m){
memset(que,1,sizeof(que));
int count = 1;//用来记录当前喊到的数字
int re = 1;//记录输出的数量
int cur = k;//记录当前的数字
while(1){
if(cur==n+1)cur = 1;//超出范围则返回第一个
if(que[cur]){
if(count == m){//该标记了
que[cur] = 0;
out[re] = cur;
re++;
count = 1;
if(check(n,que))break;
}
else count++;
}
cur++;
}
for(int j = 1;j < re;j++){
if(j%10==0){
printf("%d\n",out[j]);
}
else printf("%d",out[j]);
}
printf("\n");
}
int main()
{
int n, k ,m;
scanf("%d%d%d",&n,&k,&m);
if(n < 0 || k < 0 || m <0){
printf("n,k,m must bigger than 0.");
}
else if(n < k)printf("k should not bigger than n.");
else solve(n,k,m);
}
结果是:
9 3 2
4
只有一个输出。
#include <stdio.h>
#include <string.h>
#define N 1000
int que[N];
int out[N];
int n, k, m;
int check()
{
for (int i = 1; i <= n; i++)
{
if (que[i] == 1)
return 0;
}
return 1;
} //检查是否每一个数字都被标记
void solve()
{
for(int i = 0; i <= N; i++) que[i] = 1;
int count = 1; //用来记录当前状态
int re = 1; //记录输出的数量
int cur = k;
while (1)
{
if (cur == n + 1)
cur = 1; //超出则返回
if (que[cur])
{
if (count == m)
{ //该标记了
que[cur] = 0;
out[re] = cur;
re++;
count = 1;
if (check())
break;
}
else
count++;
}
cur++;
}
for (int j = 1; j < re; j++)
{
if (j % 10 == 0)
{
printf("%d\n", out[j]);
}
else if(j != re -1)
printf("%d ", out[j]);
else if(j == re -1)printf("%d\n",out[j]);
}
}
int main()
{
scanf("%d,%d,%d", &n, &k, &m);
if (n < 0 || k < 0 || m < 0)
{
printf("n,k,m must bigger than 0.\n");
}
else if (n < k)
printf("k should not bigger than n.\n");
else
solve();
}
大多数样例都过了。但是有超时的样例。并且数组也开小了。
首先反思memset函数的错误使用。其次,貌似只能用链表去做了。
好家伙,一定要认真读题啊。输入错误的判断写错了。
if (n < 1 || k < 1 || m < 1)
{
printf("n,m,k must bigger than 0.\n");
}
else if (n < k)
printf("k should not bigger than n.\n");
else
solve(head);
这样就可以过了
想法二 循环链表
#include <stdio.h>
#include<stdlib.h>
#include <string.h>
#define N 1000
int n, k, m;
int out[N];
typedef struct LNode //定义单链表结点类型
{
int data; //每个节点存放一个数据元素
struct LNode *next; //指针指向下一个节点
}LNode,*LinkList;
//建链表
LNode * CreateLink(int n)
{
LNode * h = (LNode *)malloc (sizeof(LNode)); //定义表头指针
h->next = h; //初始化
LNode * temp = (LNode *)malloc (sizeof(LNode));
temp = h;
//构建循环链表
int x = n;
int i = 1;
while(x){
LNode *s;
s = (LNode *) malloc (sizeof(LNode));
s->data = i++;
s->next = temp->next; temp->next = s;
temp = s;
x--;
}
temp->next = h->next; // 指向表头
return temp;
}
LNode * get(int k, LNode *L){//确定某一个位置
//在链表中找到编号为k的结点
LNode * temp = (LNode *)malloc (sizeof(LNode));
temp = L;
int i = 1;
while(i < k){
temp = temp->next;
i++;
}
return temp;
}
void solve(LNode *L){
//先找到开始报数的人
LNode *temp = (LNode *)malloc(sizeof(LNode));
LNode *cur = (LNode *)malloc(sizeof(LNode));
cur = get(k,L);
int num = 10;
//循环到只剩下一个人
while(cur->next != cur){
//找到要出局的人
for(int i = 1; i < m; i++){
cur = cur->next;
}
//记下本次出局的人
temp = cur->next;
cur->next = temp->next;
num--;
if(!num) {
printf("%d\n",temp->data);
num=10;
}
else
printf("%d ",temp->data);
}
printf("%d\n",cur->data);
free(cur);
free(L);
}
int main()
{
scanf("%d,%d,%d", &n, &k, &m);
LNode *head = CreateLink(n);
if (n < 1 || k < 1 || m < 1)
{
printf("n,m,k must bigger than 0.\n");
}
else if (n < k)
printf("k should not bigger than n.\n");
else
solve(head);
}
链表是一定要掌握的方法。一定要记得每一次建立一个新的节点都要分配内存!
链表法借鉴了csdn上的一些模版,按照我自己的思路写出类想法一的结构。