c++循环链表约瑟夫问题


#include <bits/stdc++.h>
using namespace std;
#define man 1
#define woman 0
#define maxnamelength 20
typedef struct lnode{
struct lnode* next;
int num,sex,age;
char name[21];
}LNode,*Linklist;
//从键盘输入人数N(N<50)及N个人的编号(整型)、姓名、性别和年龄,正向建立带头节点的循环链表;
//输出循环链表各结点的值;
void InitList(Linklist &L);
void DestroyList(Linklist &L);
bool ListInsert(Linklist L,int pos,int nu,int se,int ag,char* nam);
void PrintList(Linklist L);
bool findandcut(int &n,int s,int x,int m,Linklist L);
int main() {
int t;
printf("input t:");
scanf("%d",&t);
while(t-->0){
int n;
printf("input n: ");
scanf("%d",&n);
Linklist L=NULL;
InitList(L);
for(int i=0;i<n;i++){
int num,sex,age;
char name[21]={'\0'};
printf("input num,age,sex(1 for man and 0 for woman),name for node %d: ",i+1);
scanf("%d,%d,%d,%20s",&num,&age,&sex,name);
while(getchar()!='\n');//清除缓冲区
ListInsert(L,i+1,num,sex,age,name);
}
PrintList(L);
//输入开始报数的人的编号S、间隔的个数M和剩余人数X;
int s,m,x;
printf("input s,m,x:(m>0,x>=0) ");
for(;;){
scanf("%d,%d,%d",&s,&m,&x);
if(m>0&&x>=0)break;
printf("make sure m>0,x>=0,try again: ");
}
if(findandcut(n,s,x,m,L))printf("error");
DestroyList(L);
}
return 0;
}
void InitList(Linklist &L){
L=(Linklist)malloc(sizeof(LNode));
if(!L)exit(1);//分配失败异常退出
L->next=L;//空表标志
}
void DestroyList(Linklist &L){
for(;L->next!=L;){
Linklist p=L->next->next;
free(L->next);
L->next=p;
}
free(L);
L=NULL;
}
bool ListInsert(Linklist L,int pos,int nu,int se,int ag,char* nam){
Linklist p=L;
int cur=0;if(pos<0)exit(1);
for(;;p=p->next,cur++){
if(cur==pos-1){
//插到第pos个,就要找到第pos-1个
//q指向新建的节点
Linklist q=(Linklist)malloc(sizeof(LNode));
if(!q)exit(1);
q->num=nu;q->age=ag;
q->sex=se;strcpy(q->name,nam);
q->next=p->next;
p->next=q;
return 1;
}
if(p->next==L)break; //退出条件放在结尾,头结点至少执行一次
}
//遍历到最后一个也没找到pos,返回1 插入失败
return 0;
}
void PrintList(Linklist L){
Linklist p=L->next;
int i=1;
for(;p!=L;p=p->next,i++){
printf("node %d: num=%d name=%s sex=%s age=%d\n",i,p->num,p->name,p->sex?"man":"woman",p->age);
}
}
bool findandcut(int &n,int s,int x,int m,Linklist L){
//在循环链表中查找到编号为S的结点;
//从1开始向后报数,将报M的人(结点)从循环链表中删除,并输出该人的编号;
//从刚才被删除人的下一个人开始重复上一步,直至最后只剩下 X 个人为止;
//输出最后剩余的人的编号、姓名、性别和年龄。
Linklist p=L;
//空表则不进入循环
//1、找S ,防止m为1故从L开始找
for(;p->next!=L;p=p->next){
if(p->next->num==s){
printf("cut start:\n");
int no=1;//cut 第no个
for(;n>x;n--,no++){
int count=0;//删去报m的要找到报m-1的
int flag=0;
for(;count<m-1;p=p->next,count++){
if(p->next==L)flag++;
}
if(p->next==L)p=p->next;//头结点不能删,要跳过去
while(flag-->0)p=p->next;
//每次数到头结点,要用flag记一下,不应该数头结点
Linklist q=p->next->next;//保存删去节点的下一个
printf("NO.%d cut %d\n",no,p->next->num);
free(p->next);
p->next=q;
}
printf("remained people:\n");
PrintList(L);
return 0;
}
}
return 1;
//遍历到最后一个也没找到s,异常退出
}
测试用例(进行5组测试)
5
5
101,25,1,ZhangSan
102,30,0,LiSi
103,22,1,WangWu
104,28,0,ZhaoLiu
105,35,1,QianQi
104,3,2
3
201,20,1,Tom
202,25,0,Lucy
203,30,1,Jack
201,2,0
4
301,18,1,Student1
302,19,0,Student2
303,20,1,Student3
304,21,0,Student4
301,1,4
1
401,40,1,OnlyOne
401,5,1
2
501,22,1,Test1
502,23,0,Test2
502,-1,1
502,5,1
一、设计思路
本程序采用带头节点的循环链表数据结构来解决约瑟夫问题。主要设计思路如下:
- 数据结构选择:使用带头节点的循环链表来存储人员信息,头节点不存储实际数据,仅作为链表起始标志
- 模块化设计:将程序功能分解为初始化、插入、打印、删除、销毁等独立函数
- 约瑟夫算法:从指定编号开始报数,每隔M个人删除一个节点,直到剩余X个人为止
- 内存管理:动态分配内存,使用后及时释放,避免内存泄漏
- 输入验证:对用户输入进行合法性检查,确保程序健壮性
二、代码说明
1、结构体定义
typedef struct lnode{
struct lnode* next; // 指向下一个节点的指针
int num,sex,age; // 编号、性别、年龄
char name[21]; // 姓名(最大20字符)
}LNode,*Linklist;
2、宏定义
define man 1 // 男性标识
define woman 0 // 女性标识
define maxnamelength 20 // 姓名最大长度
3、函数功能说明
3.1 void InitList(Linklist &L)
- 功能:初始化带头节点的循环链表
- 参数:链表头指针的引用
- 实现:分配头节点内存,设置next指向自身,标志空表
3.2 void DestroyList(Linklist &L)
- 功能:销毁整个链表,释放内存
- 参数:链表头指针的引用
- 实现:循环释放所有节点,最后释放头节点
3.3 bool ListInsert(Linklist L,int pos,int nu,int se,int ag,char* nam)
- 功能:在指定位置插入新节点
- 参数:链表头指针、插入位置、编号、性别、年龄、姓名
- 返回值:成功返回true,失败返回false
3.4 void PrintList(Linklist L)
- 功能:打印链表中所有节点的信息
- 参数:链表头指针
- 输出格式:节点序号、编号、姓名、性别、年龄
3.5 bool findandcut(int &n,int s,int x,int m,Linklist L)
- 功能:执行约瑟夫问题的核心算法
- 参数:当前人数n(引用)、起始编号s、剩余人数x、间隔m、链表头指针
- 返回值:找到起始节点返回false,未找到返回true
- 算法:定位起始节点后,循环报数删除节点,直到剩余指定人数
三、运行结果

五、程序特点
1. 健壮性:对输入参数进行合法性检查,避免非法操作
2. 模块化:各功能独立封装,代码结构清晰
3. 内存安全:动态分配的内存都能正确释放
4. 用户友好:清晰的输入提示和输出格式
5. 算法正确:约瑟夫问题求解逻辑准确,处理各种边界情况
更多推荐


所有评论(0)