约瑟夫环
Title: 约瑟夫环问题
Date: 2017-03-10 22:38:52
Category: 数据结构
Tags: 链表
Slug: yuesefuhuan
Author: lart
Illustration: 数据结构.jpg
问题:
约瑟夫环问题:
问题描述:设有编号为1,2,3……n的n个人顺时针方向围坐一圈,每人有一密码(正整数)。开始时给出一报数上限m,从编号为1的人开始报数,报m的人出列;以后将出列者的密码作为新的m,从顺时针方向紧挨着他的下一个人开始报数……直至所有人出列。试编算法,求出出列顺序。
要求:
1. 用不带头结点的单向循环链表实现
2. 从键盘输入n,m
3. 各人的密码由计算机随机产生(1~10的正整数,也可以自己指定)
测试数据:
假设m的初值为6;n=7。7个人的密码依次是:3,1,7,2,4,8,4 则出列的顺序为: 6,1,4,7,2,3,5
1.0 版本
# include <stdio.h>
# define NUM 7 //人的总个数
int main()
{
int members[1 + NUM] = { 0, 3, 8, 1, 22, 4, 9, 15 };//间隔多少个人
//将各个成员的存在状态用一个仅有0和1的int数组表述
//1存在;0退出
int bool[1 + NUM];//存在状态数组
int *p = bool;//循环指针
int i_1;//循环指数
for (i_1 = 1; i_1 <= NUM; i_1++)//初始化
*(p + i_1) = 1;
//给定第一步进数
int init_step = 5;
//先将特殊的第一个处理掉,后面的有规律
int i_2;//循环指数
if (init_step % NUM != 0)//有余数
{
*(p + init_step % NUM) = 0;
i_2 = init_step % NUM;
}
else//无余数
{
*(p + NUM) = 0;
i_2 = NUM;
}
printf("退出:%d \n", i_2);
//处理后面的数据
int j = 0; //j统计经过的未退出的人数,至上一个退出人的数据后,归零
int count = 1; //统计退出的人数
int death; //退出者的序号
death = i_2;
for (;; ++i_2)
{
if (*(p + i_2) == 1)//此人还活着
{
j++;
if (j == members[death])
{
*(p + i_2) = 0;
death = i_2;
j = 0;
count++;
printf("退出:%d \n", i_2);
}
if (count == NUM)
{
printf("最后退出:%d \n", i_2);
break;
}
}
if (i_2 == NUM)
i_2 = 0;
}
return 0;
}
2.0 版本
//使用循环链表来解决约瑟夫环问题
//需要对链表实现的功能:
//1. 初始化链表节点值
//2. 删掉对应元素
//3. 返回对应值和序号
#include <stdio.h>
#include <stdlib.h>
#define NUM 7
#define TRUE 1
#define FALSE 0
#define OK 1
#define ERROR 0
#define INFEASIBLE -1
#define OVERFLOW -2
typedef int Status;
typedef struct LinkNode{
int number;// 序号
int data;// 对应的步进
struct LinkNode* next;// 指向下一个节点的指针
}LinkNode, *LinkList;// 指向结构体的指针
int members[NUM] = { 3, 8, 1, 22, 4, 9, 15 };
// 设立尾指针的单循环链表
Status ListInit_CL(LinkList &L)
{
LinkList l = (LinkList)malloc(sizeof(struct LinkNode));
if (!l)
exit(OVERFLOW);
L = l;// L就当作首,方便最后传出时,L没有变化
int index;
for (index = 0; index < NUM-1; index++)// 循环赋值
{
l->data = members[index];
l->number = index + 1;
LinkList q = (LinkList)malloc(sizeof(struct LinkNode));// 临时存储
if (!q) // 分配失败
return OVERFLOW;
l->next = q;
l = q;// 移位,当在最后一次的循环中,l所在是空的
q = NULL;
free(q);
}
l->data = members[index];
l->number = index + 1;
l->next = L;
return OK;
}
// 删除第i个元素,并由number,data返回其序号和值
Status ListDelete_CL(LinkList &L, int i, int &number, int &data)
{
LinkList q;
int j;
for (j = 0; j < i - 1; j++)// 移动i-1次,寻找后面第i-1个结点
L = L->next;
q = L->next;// q指向待删除结点
L->next = q->next;// L->next指向被删除节点的下一个,后面的计数就是从L现在往后第i个了
//返回被删除节点序号与值
data = q->data;
number = q->number;
free(q);// 释放待删除结点
return OK;
}
int main()
{
LinkList L = NULL;// 头指针,指向类似的结构体的指针
if (!ListInit_CL(L))
return ERROR;
int i, get_data, get_number, given_num;
printf("请输入你想要的数字:\n");
scanf("%d", &given_num);
ListDelete_CL(L, given_num-1, get_number, get_data);// 删除第一个人对应的下一个人
printf("第%d位被剔除,他的信息是%d\n", get_number, get_data);
int index;
for (index = 0; index < NUM - 2; index++)// 只删除n-2个,除了前面删除的一个,最后剩下的就是没有删除的。
{
ListDelete_CL(L, get_data, get_number, get_data);
printf("第%d位被剔除,他的信息是%d\n", get_number, get_data);
}
printf("第%d位被剩下\n", L->number);
return OK;
}
3.0 版本
//使用循环链表来解决约瑟夫环问题
//需要对链表实现的功能:
//1. 初始化链表节点值
//2. 删掉对应元素
//3. 返回对应值和序号
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#define TRUE 1
#define FALSE 0
#define OK 1
#define ERROR 0
#define INFEASIBLE -1
#define OVERFLOW -2
typedef int Status;
typedef struct LinkNode{
int number;// 序号
struct LinkNode* next;// 指向下一个节点的指针
}LinkNode, *LinkList;// 指向结构体的指针
// 设立尾指针的单循环链表
Status ListInit_CL(LinkList &L, int given_people)
{
LinkList l = (LinkList)malloc(sizeof(struct LinkNode));
if (!l)
exit(OVERFLOW);
L = l;// L就当作首,方便最后传出时,L没有变化
int index;
for (index = 0; index < given_people-1; index++)// 循环赋值
{
l->number = index + 1;
LinkList q = (LinkList)malloc(sizeof(struct LinkNode));// 临时存储
if (!q) // 分配失败
return OVERFLOW;
l->next = q;
l = q;// 移位,当在最后一次的循环中,l所在是空的
q = NULL;
free(q);
}
l->number = index + 1;
l->next = L;
return OK;
}
// 删除第i个元素,并由number返回其序号
Status ListDelete_CL(LinkList &L, int i, int &number)
{
LinkList q;
int j;
for (j = 0; j < i - 1; j++)// 移动i-1次,寻找后面第i-1个结点
L = L->next;
q = L->next;// q指向待删除结点
L->next = q->next;// L->next指向被删除节点的下一个,后面的计数就是从L现在往后第i个了
//返回被删除节点序号
number = q->number;
free(q);// 释放待删除结点
return OK;
}
int main()
{
int i, get_number, given_people, given_maxnum;
printf("请输入人数n,报数上限m\n");
scanf("%d %d", &given_people, &given_maxnum);
if (given_people <= 0 || given_maxnum <= 0)
return ERROR;
LinkList L = NULL;
if (!ListInit_CL(L, given_people))
return ERROR;
srand((unsigned)time(NULL));
int rand_number;
ListDelete_CL(L, rand_number = rand() % given_maxnum + 1 - 1, get_number);
printf("第%d位被剔除,对应的随机数是%d\n", get_number, rand_number + 1);
int index;
for (index = 0; index < given_people - 2; index++)// 只删除n-2个,除了前面删除的一个,最后剩下的就是没有删除的。
{
ListDelete_CL(L, rand_number = rand() % given_maxnum + 1, get_number);
printf("第%d位被剔除,对应的随机数是%d\n", get_number, rand_number);
}
printf("第%d位被剩下\n", L->number);
return OK;
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 基于Microsoft.Extensions.AI核心库实现RAG应用
· Linux系列:如何用heaptrack跟踪.NET程序的非托管内存泄露
· 开发者必知的日志记录最佳实践
· SQL Server 2025 AI相关能力初探
· 震惊!C++程序真的从main开始吗?99%的程序员都答错了
· winform 绘制太阳,地球,月球 运作规律
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
· 上周热点回顾(3.3-3.9)
· 超详细:普通电脑也行Windows部署deepseek R1训练数据并当服务器共享给他人