约瑟夫问题两种实现方式
数组版本
#include<stdio.h> #include <stdlib.h> main(){ int len; int pace; int time; int start; printf("请输入人数:\n"); scanf("%d",&len); if(len<=0) { printf("输入错误,程序终止!\n"); exit(-1); } int *p=(int*)malloc(len*sizeof(int)); printf("请输入步长:\n"); scanf("%d",&pace); if(pace==0) { printf("输入错误,重新输入!\n"); exit(-1); } //if(pace==0) printf("请输入进行轮数:\n"); scanf("%d",&time); printf("请输入您想从那个元素开始:\n"); scanf("%d",&start); if(start==0) { printf("输入错误,请重新输入:\n"); exit(-1); } for(int i=0;i<len;i++){*(p+i) = 1;} // 数组初始化,1:表示为活着;0:表示自杀 int leftCount = len; // 计数器leftCount:计数剩下的人 int index = start-1,count = 0; // 1.数组下标index;2.循环计数器count while(leftCount>(len-time))//从规定元素起点开始 { // 当还剩下n个结束 if(*(p+index) == 1){ count++; if(pace== count){ // 计数到3,1.自杀;2.循环计数器count重新开始计数;3.计数器leftCount减1 *(p+index) = 0; count = 0; leftCount--; } } index++; if(index == len){index = 0;} // 当到数组尾,数组下标index置零,重新开始 } for(int j=0;j<len;j++){ // 输出结果 if(1 == *(p+j)){ printf("剩下的人为第%d个\n",j+1); } } }
单向循环链表版本
# include <stdio.h> # include <stdlib.h> typedef struct circle_list{ int data; struct circle_list *next; }NODE,*PNODE;//定义节点 PNODE creat_list(int len);//创建循环链表 void visit_list(PNODE mark,int len,int time);//遍历寻欢链表 PNODE kill_list(PNODE head,int pace,int time,int len,int mark);//删除循环链表的一个给定下表的元素 main() { int pace;//步长 int time;//轮数 int len;//循环链表的长度 int index;//开始游戏的下标 PNODE mark=NULL;//访问遍历链表的指针 PNODE head=NULL;//创建循环链表的头 int i; printf("在罗马人占领乔塔帕特后,\n"); printf("39 个犹太人与Josephus及他的朋友躲到一个洞中,\n"); printf("39个犹太人决定宁愿死也不要被敌人抓到,\n"); printf("于是决定了一个自杀方式,41个人排成一个圆圈,由第1个人开始报数,\n"); printf("每报数到第3人该人就必须自杀,然后再由下一个重新报数,直到所有人都自杀身亡为止。\n"); printf("现在您就是约瑟夫,为了活下去,您需要预测怎样才能活到最后\n"); printf("而此时您得到了一个神奇的程序,请借助它,活下去!\n"); printf("********************************************************************************\n"); printf("欢迎来到约瑟夫问题,游戏开始!\n"); printf("请输入总人数:\n"); scanf("%d",&len); if(len==0) { printf("输入错误,程序结束\n"); exit(-1); } head=creat_list(len);//创建链表 printf("请输入您想从第几个人开始:\n"); scanf("%d",&index); printf("请输入步长(每经过几个人杀一个人:)\n"); scanf("%d",&pace); printf("请输入进行的第几轮:\n"); scanf("%d",&time); mark=kill_list(head,pace,time,len,index);//杀人并提供访问遍历链表的下标 visit_list(mark, len,time);//遍历链表 } PNODE creat_list(int len) { PNODE head=NULL; PNODE p; PNODE q; int i; p=(PNODE)malloc(sizeof(NODE)); if(p==NULL) { printf("内存分配失败!\n"); exit(-1); } head=p; p->data=1;//创建头结点 并且将它的数据域置为1 for(i=2;i<=len;i++)//创建其他节点并依次将这些节点挂在尾部 { q=(PNODE)malloc(sizeof(NODE)); if(q==NULL) { printf("内存分配失败!\n"); exit(-1); } q->data=i;//依次给节点数据域赋值,作为“人”的序号 p->next=q; p=q;//让p永远指向链表的尾部 } p->next=head;//首尾相连 return head;//返回头结点地址以便访问 } void visit_list(PNODE mark,int len,int time)//遍历循环链表 { PNODE p=mark;//从标记开始进行循环 int count=0; printf("幸存下来人的序号为:\n"); do { printf("%d ",p->data);//依次输出未被删除节点的序号 p=p->next; } while(p!=mark);//当没有循环够一圈 printf("\n"); } PNODE kill_list(PNODE head,int pace,int time,int len,int index) { int leftcount=len;//活着的人数 PNODE p=head; PNODE mark=NULL;//留下以便访问的地址 PNODE remain=NULL;//作为用来防止内存泄漏的媒介 int count=0;//循环次数 int count1=0;//从开始游戏的那个人开始循环次数 while(leftcount>(len-time)) { count++; if(count>=index)//当过了要开始下表才开始杀人 { count1++; if(count1%(pace-1)==0)//当循环到要删除的那个节点的上一个节点才开始删除下一个节点 { mark=p;//不断刷新下表让它永远是最后留在链表中节点的地址即mark所指向的那个节点删到最后一定要存在 remain=p->next; p->next=p->next->next;//改变指针指向来删除节点 free(remain); leftcount--;//人数减少一位 } } p=p->next;//遍历条件 } return mark; }