约瑟夫环(顺序表)
约瑟夫环的知识点总结
约瑟夫环的知识点总结约瑟夫环这个问题不仅在古代受到了广泛的关注,而且在现代数学中也有着重要的地位。
它涉及到了排列、递推、循环和递归等多个数学概念,并且有着一些有趣的数学特性。
因此,学习约瑟夫环不仅能够增加我们对于数学问题的理解,而且也可以提高我们的数学思维能力。
接下来,我们将从几个方面对约瑟夫环进行深入的讨论。
1. 约瑟夫环的历史约瑟夫环最早出现在约瑟夫斯的《犹太古记》中,他描述了犹太人在与罗马军队的战斗中围攻马萨达城的情景。
根据《犹太古记》的记载,当罗马军队攻陷了马萨达城后,大约960名男子决定宁死不从。
于是,他们站成一个圈,每隔两个人就有一个杀掉,直到最后只剩下一个人。
而这个幸存者恰恰就是约瑟夫斯本人。
因此,这个问题就得名为约瑟夫环。
除了这个故事之外,约瑟夫环在古代数学文献中也有着多次的提及。
例如,中国古代数学家秦九韶在其著作《数书九章》中也提到了这个问题。
他利用递推的方法解出了约瑟夫环的一般解,并推广到了更一般的情况。
自古代以来,约瑟夫环一直受到数学家们的关注,他们提出了很多不同的方法来解决这个问题。
而到了现代,约瑟夫环在计算机科学和密码学中也有着广泛的应用。
因此,约瑟夫环问题可以说是一个古老而又具有重要意义的数学问题。
2. 约瑟夫环的一般解在数学中,我们可以用递推的方法对约瑟夫环进行求解。
假设有N个人站成一圈,编号从0到N-1,而每隔M个人就有一个人出列。
那么一个简单直接的方法就是用递归来求解。
具体来说,我们可以定义一个递归函数f(n, m),表示N个人中最后存活下来的那个人的编号。
那么这个函数的递归关系可以如下定义:f(n, m) = (f(n-1, m) + m) % n其中f(1, m) = 0,表示只有一个人时的情况。
通过递归的方法,我们可以得到约瑟夫环的一般解。
而根据这个递归关系,我们还可以得到一些有趣的数学性质。
例如,我们可以求解约瑟夫环在给定N和M的情况下的解,而不需要实际模拟整个过程。
约瑟夫环问题(Josephus)
算法设计
Josephus jp=new Josephus(); int a[]=new int[n]; for(int i=0;i<n;i++){ a[i]=i+1; } jp.SortArray(a,n,m,k,g); } public void show(int[]b,int g){ for(int i=b.length-g;i<b.length;i++){ System.out.print(b[i]+" "); } }
• b[c]=a[i]; • a[i]=0; • c++; • if(c==n) break; • } • System.out.print(“最后出列的 3人: "); • this.show(b,g); • } • }
• 1.数据选择: 要求:n<2^15; 1<=k<=n; 2.数据和结果显示:
(3)当然其中还是会存在一些漏洞,需要进 一步的改进。在计算机中是容不得丝毫的 错误的,这也让我们学到了面对科学要持 有严谨的态度,否则必定得不到应该有的 结果。
总人数n 起始号码k 循环数m 最后出列的3人 总人数n 起始号码k 循环数m 最后出列的3人 总人数n 起始号码k 循环数m 最后出列的3人 48 6 15 47 21 46 105 73 4 87 32 21 300 80 12 70 296 198 总人数n 起始号码k 循环数m 68 34 25
输出格式:
T行最后min(n,3)个出列的编号。 结果:6 1 5
问题背景
• 这个问题是以弗拉维奥•约瑟夫斯命名的, 它是1世纪的一名犹太历史学家。他在自己 的日记中写道,他和他的40个战友被罗马 军队包围在洞中。他们讨论是自杀还是被 俘,最终决定自杀,并以抽签的方式决定 谁杀掉谁。约瑟夫斯和另外一个人是最后 两个留下的人。约瑟夫斯说服了那个人, 他们将向罗马军队投降,不再自杀。
数据结构课后习题及解析第二章
例如m的初值为20;n=7,7个人的密码依次是:3,1,7,2,4,8,4,出列的顺序为6,1,4,7,2,3,5。
第二章答案
约瑟夫环问题
约瑟夫问题的一种描述为:编号1,2,…,n的n个人按顺时针方向围坐一圈,每个人持有一个密码(正整数)。一开始任选一个报数上限值m,从第一个人开始顺时针自1开始顺序报数,报到m时停止报数。报m的人出列,将他的密码作为新的m值,从他在顺时针方向上的下一个人开始重新从1报数,如此下去,直至所有的人全部出列为止。试设计一个程序,求出出列顺序。利用单向循环链表作为存储结构模拟此过程,按照出列顺序打印出各人的编号。
9.假设有一个循环链表的长度大于1,且表中既无头结点也无头指针。已知s为指向链表某个结点的指针,试编写算法在链表中删除指针s所指结点的前趋结点。
10.已知有单链表表示的线性表中含有三类字符的数据元素(如字母字符、数字字符和其它字符),试编写算法来构造三个以循环链表表示的线性表,使每个表中只含同一类的字符,且利用原表中的结点空间作为这三个表的结点空间,头结点可另辟空间。
r=p;
}
}
r->next=L->next;
printf("请输入第一个报数上限值m(m>0):");
scanf("%d",&m);
printf("*****************************************\n");
printf("出列的顺序为:\n");
q=L;
p=L->next;
7.试分别以不同的存储结构实现线性表的就地逆置算法,即在原表的存储空间将线性表(a1, a2..., an)逆置为(an, an-1,..., a1)。
约瑟夫环问题源代码(C语言)
约瑟夫环问题如下:已知n个人(n>=1)围桌一园桌周围,从1开始顺序编号。
从序号为1的人开始报数,顺时针数到m的那个人出列。
他的下一个人又从1开始报数,数到m的那个人又出列。
依此规则重复下去,直到所有人全部出列。
求解最后一个出列的人的编号。
本次实验是以顺序表求解约瑟夫环问题,程序流程图及程序运行结果如下:输入人数、所报数、第一个报数人编号存储并建立一个约瑟夫环通过循环结构依次查找每次出列的人的编号并输出输出最后一个出列的人的编号程序代码如下:#include<iostream>#include<process.h>#include<stdlib.h>using namespace std;struct Node //循环节点的定义{int number; //编号Node *next;};Node *CreateList(Node *L,int &n,int &m); //建立约瑟夫环函数void Joseph(Node *L,int n,int m); //输出每次出列号数函数Node *DeleteList(Node **L,int i,Node *q); //寻找每次出列人的号数int LengthList(Node *L); //计算环上所有人数函数void main() //主函数{system("color 75"); //设置颜色以美观Node *L;L=NULL; //初始化尾指针int n, m;cout<<"请输入人数N:";cin>>n; //环的长度if(n<1){cout<<"请输入正整数!";} //人数异常处理else{cout<<"请输入所报数M:";cin>>m;if(m<1){cout<<"请输入正整数!";} //号数异常处理else{L=CreateList(L,n,m); //重新给尾指针赋值Joseph(L,n,m);}}system("pause");}Node *CreateList(Node *L,int &n,int &m) //建立一个约瑟夫环(尾插法){Node *q;for(int i=1;i<=n;i++){Node *p;p=new Node;p->number=i;p->next=NULL;if(i==1) L=q=p; //工作指针的初始化 else{q->next=p;q=q->next;}}q->next=L;if(L!=NULL){return(L);} //返回尾指针else cout<<"尾指针异常!"<<endl; //尾指针异常处理}void Joseph(Node *L,int n,int m) //输出每次出列的人{int k;cout<<"请输入第一个报数人:";cin>>k;if(k<1||k>n){cout<<"请输入1-"<<n<<"之间的数"<<endl;}else{cout<<"\n出列顺序:\n";for(int i=1;i<n;i++){Node *q = new Node;if(i==1) q=DeleteList(&L,k+m-1,q); //第一个出列人的号数else q=DeleteList(&L,m,q);cout<<"号数:"<<q->number<<endl;delete q; //释放出列人的存储空间}cout<<"最后一个出列号数是:"<<L->number<<endl; //输出最后出列人的号数}}Node *DeleteList(Node **L,int i,Node *q) //寻找每次出列的人{if(i==1) i+=LengthList(*L); //顺序依次出列情况的处理方式Node *p;p=*L;int j=0;while(j<i-2) {p=p->next;j++;}q = p->next;p->next=p->next->next;*L = p->next;return(q);}int LengthList(Node *L) //计算环上的人数{if(L){cout<<"尾指针错误!"<<endl;} //异常处理else{int i=1;Node *p=L->next;while(p!=L){i++;p=p->next;}return(i);}}实验体会:通过对本问题的分析,我进一步熟悉了对各种逻辑表达式的判断和指针的使用。
抽杀问题 约瑟夫问题
[阅读材料]世界名题与小升初之:抽杀问题(約瑟夫问题)--马到成功老师在各类竞赛中,各类小升初考试中相关的世界名题出现的概率极高,这是由小升初与数学竞赛的特点决定,这特点便是:知识性,趣味性,思想性相结合。
先给大家介绍这一问题的由来。
据说著名犹太历史学家Josephus有过以下的故事:在罗马人占领乔塔帕特後,39 個犹太人与Josephus及他的朋友躲到一個洞中,39個犹太人決定宁愿死也不要被人抓到,于是決定了一个自杀方式,41個人排成一个圆圈,由第1個人开始报数,每报数到第3人该人就必須自杀,然后再由下一个重新报数,直到所有人都自杀身亡为止。
然而Josephus 和他的朋友并不想遵从,Josephus要他的朋友先假装遵从,他將朋友与自己安排在第16個与第31個位置,于是逃过了这场死亡游戏。
解法約瑟夫问题可用代数分析來求解,将这个问题扩大好了,假设现在您与m个朋友不幸参与了这个游戏,您要如何保护您的朋友?只要画两个圆圈就可以让自己与朋友免于死亡游戏,这两个圆内圈是排列顺序,而外圈是自杀顺序,如下图所示:使用程式来求解的话,只要将阵列当作环状来处理就可以了,在陈列中由计数1开始,每找到三个无资料区就填入一个计数,直接计数來求解的話,只要將阵列当作环状来处理就可以了,在阵列中由計数1开始,每找到三个无资料区就填入一个計数,直而計数达41为止,然后將阵列由索引1开始列出,就可以得知每个位置的自杀順序,这就是約瑟夫排列,41個人报数3的約瑟夫排列如下所示:14 36 1 38 15 2 24 30 3 16 34 4 25 17 5 40 31 6 18 26 7 37 19 8 35 27 9 20 32 10 41 21 11 28 39 12 22 33 13 29 23由上可知,最后一個自杀的是在第31个位置,而倒数第二个自杀的要排在第16个位置,之前的人都死光了,所以他们也就不知道約瑟夫与他的朋友并没有遵守游戏规则了。
关于约瑟夫环的解法
关于约瑟夫环的解法自己曾经考过三级遇到过一些问题,自己也琢磨过其中有一道排队的题目叫“约瑟夫环”。
自己曾经查过一些资料答案有几种有的用链表解决思路清晰,但对初学链表的人却不是很好理解,有的用数组排列的方法解决程序干净简洁但其中有一段对初学者也较为困难。
经过我自己研究我找到了一种理解起来相对较容易的方法,可能程序运行效率不如上面第二种高但对有一定基础的初学者还是比较容易的。
首先我们看一道题目:有一组数,1,2,3,4,5,6。
请通过数组元素移动的方法将其变为4,5,6,1,2,3。
很简单:理解了上面这道题,约瑟夫环就快解决了;按照题目的要求我们是要把报到M的人提出来排队,如果我们把报到M的人放到到最后一位,并且下一次报数时不让他参与问题就简单了我们假设要把报道"3"的人踢出队伍,而且从第一个人开始报数1←2←3←4←5←6←7←8←9←10;2←3←4←5←6←7←8←9←10←13←4←5←6←7←8←9←10←1←24←5←6←7←8←9←10←1←2←3经过第一轮报数得:4←5←6←7←8←9←10←1←2←3其实这个过程是让整个队伍向前走,第一个人走到队尾第二个人跟到第一个人后面,第三个人跟到第二个人后面,当第三个人走到对尾时第一轮结束,并且剩下的九个人准备开始下一轮这就犹如一个游戏:有一对人编号为1——10,游戏开始第一个人报"1"完成此轮报数并且走到对尾,第二个人报"2"完成此轮报数并且走到对尾,第三个人报"3"完成此轮报数,走到现有10人的对尾并且不再参加下一轮报数。
剩余九人保持现有相对顺序不变,继续按照前述规则进行,此轮报到"3"的人站在现有9人的对尾。
如此继续,报数的人越来越少,报过"3"的人也自成一队列。
第一轮后的结果:4←5←6←7←8←9←10←1←2←3;5←6←7←8←9←10←1←2←4←36←7←8←9←10←1←2←4←5←37←8←9←10←1←2←4←5←6←3第二轮结果:7←8←9←10←1←2←4←5←6←3束后对尾那个报"3"的人不再参加下一轮报数。
C语言的循环链表和约瑟夫环
C语言的循环链表和约瑟夫环C语言的循环链表和约瑟夫环约瑟夫问题)是一个数学的应用问题,对于学习C语言四非常挺有帮助的,下面是店铺为大家搜集整理出来的有关于C语言的循环链表和约瑟夫环,一起了解下吧!循环链表的实现单链表只有向后结点,当单链表的尾链表不指向NULL,而是指向头结点时候,形成了一个环,成为单循环链表,简称循环链表。
当它是空表,向后结点就只想了自己,这也是它与单链表的主要差异,判断node->next是否等于head。
代码实现分为四部分:1. 初始化2. 插入3. 删除4. 定位寻找代码实现:1 2 3 4 5 6 7 8 9 1 0 1 1 1 2 1 3 1void ListInit(Node *pNode){int item;Node *temp,*target;cout<<"输入0完成初始化"<<endl; cin="">>item;if(!item)return ;if(!(pNode)){ //当空表的时候,head==NULLpNode = new Node ;if(!(pNode))exit(0);//未成功申请pNode->data = item;pNode->next = pNode;}else{//for(target = pNode;target->next!=pNode;target = target->next);4 15 16 17 18 19 2 0 2 1 2 2 2 3 2 4 2 5 2 6 2 7 2 8 2 9 3 0 3 1 3 2 3 3 3 4 3 5 3temp = new Node;if(!(temp))exit(0);temp->data = item;temp->next = pNode;target->next = temp;}}}void ListInsert(Node *pNode,int i){ //参数是首节点和插入位置Node *temp;Node *target;int item;cout<<"输入您要插入的值:"<<endl; cin="">>item;if(i==1){temp = new Node;if(!temp)exit(0);temp->data = item;for(target=pNode;target->next != pNode;target = target->next);temp->next = pNode;target->next = temp;pNode = temp;}else{target = pNode;for (int j=1;j<i-1;++j) target="target-">next;temp = new Node;if(!temp)exit(0);temp->data = item;temp->next = target->next;target->next = temp;}}void ListDelete(Node *pNode,int i){Node *target,*temp;if(i==1){for(target=pNode;target->next!=pNode;target=target ->next);temp = pNode;//保存一下要删除的首节点 ,一会便于释放6 37 38 39 4 0 4 1 4 2 4 3 4 4 4 5 4 6 4 7 4 8 4 9 5 0 5 1 5 2 5 3 5 4 5 5 5 6 5 7 5pNode = pNode->next;target->next = pNode;temp;}else{target = pNode;for(int j=1;j<i-1;++j) target="target-">next;temp = target->next;//要释放的nodetarget->next = target->next->next;temp;}}int ListSearch(Node *pNode,int elem){ //查询并返回结点所在的位置Node *target;int i=1;for(target = pNode;target->data!=elem && target->next!= pNode;++i)target = target->next;if(target->next == pNode && target->data!=elem)return 0;else return i;}</i-1;++j)></i-1;++j)></endl;></endl;>5 96 0 6 1 6 2 6 3 6 4 6 5 6 6 67 68 69 7 0 7 1 7 2 7 3 7 4 7 5 7 6 7 7 7 8 7 9 8约瑟夫问题约瑟夫环(约瑟夫问题)是一个数学的'应用问题:已知n个人(以编号1,2,3…n分别表示)围坐在一张圆桌周围。
约瑟夫环经典问题的几种算法比较
始 , 新 从 1 时针 报 数 , m 的 人 , 令 其 出 列 。 此 重 顺 报 再 如 下 去 , 到 罔 中所 有 人 出列 为止 。求 出 列 编 号 序 列口 直 。
2 循 环 顺 序 表 实现 的 算 法
2 1 算 法一 .
计 算 机
^
③ 重 复② 步 , 直到 圈中所有元 素f列为止 。 【 J
算法 如下 :
vi Jsp u (i uaS q i L lmety e Re od oe h s Cr lre Ls l c t ,ee nT p —
上 。而原来 第 i1个至倒数 第 i + 个元 素依 次 向前 移动
第
二
总数 ,o n 为计 数器【— , 为约瑟夫 环数组 下标 cu t l m】i
收 稿 日期 : 0 7 0 3 修 稿 日期 : 0 7 1 — 9 2 0 —1 — 0 20 — 2 2
it,p s ; n j o=1 i.
七
五
期
作 者 简 介 : 永 红 (9 6 , , 苏泰 州 人 , 教 授 , - , 究 方 向 为 软 件 技 术 、 王 1 6 一) 男 江 副 硕k 研 - 多媒 体 网 络 技 术
线 性 表 的 理 解 与 应 用 大 有 裨 益
whl( ta ln{ i OuT t < ) e a
w i (o n< &&OuT tl= ) hl c u tm e taa n{ <
(%() l i n+ ; ) i (- d t[) fL > aai l ]
c ount ++;
i(o= o fp s: )
顺序表实现约瑟夫环的问题,C语言
顺序表实现约瑟夫环的问题,C语言计算机科学与工程学院《算法与数据结构》试验报告[一] 专业班级 10级计算机工程02 试验地点计算机大楼计工教研室学生学号 1005080222 指导教师蔡琼学生姓名肖宇博试验时间 2012-2-29试验项目算法与数据结构试验类别基础性() 设计性() 综合性(?) 其它( )(1)掌握用VC++上机调试线性表的基本方法; 试(2)掌握顺序表的存储结构以及基本运算的实现。
验目的及要求成绩评定表类别评分标准分值得分合计积极出勤、遵守纪律上机表现 30分主动完成设计任务程序代码规范、功能正确程序与报告 70分报告详实完整、体现收获备注:评阅教师:日期: 年月日计算机科学与工程学院试验内容一、实验目的和要求1、实验目的:(1)掌握用VC++上机调试线性表的基本方法;(2)掌握顺序表的存储结构以及基本运算的实现。
2、实验内容约瑟夫环问题:设编号为1,2,3,……,n的n(n>0)个人按顺时针方向围坐一圈,m为任意一个正整数。
从第一个人开始顺时针方向自1起顺序报数,报到m时停止并且报m的人出列,再从他的下一个人开始重新从1报数,报到m时停止并且报m的人出列。
如此下去,直到所有人全部出列为止。
要求设计一个程序模拟此过程,对任意给定的m和n,求出出列编号序列。
3、实验要求:用顺序表实现。
二、设计分析根据实验要求,采用顺序表来完成本次实验。
实验中定义了两个顺序表,一个用来存储n个人的序号,另一个用来存储n个人的出队顺序及序号。
程序中充分考虑了如果出队的元素大于队列的元素个数时应该有的情况,如果出现这样的错误就提示~否则继续出队~三、源程序代码#include<stdio.h>#include<stdlib.h>#define MAXSIZE 10 // 宏替换最大值typedef struct{int data[MAXSIZE];int length;}Sqlist;void CreatList(Sqlist *&L,int a[],int n) //创建顺序表{L=(Sqlist *)malloc(sizeof(Sqlist));for(int i=0;i<n;i++){L->data[i]=a[i];}L->length=n;}void InitList(Sqlist *&L) //初始化顺序表{2 《算法与数据结构》试验报告计算机科学与工程学院L=(Sqlist *)malloc(sizeof(Sqlist));L->length=0;}void DestoryList(Sqlist *&L) //释放顺序表空间{free(L);}void josephus(Sqlist *&L) //约瑟夫环的核心代码{int t=0;int m=0;printf("请输入数到几个人出来");printf("\n");scanf("%d",&m);if(m>L->length){printf("没有这么多人呀~?(?_?)?");}else{printf("出列顺序为:");for(int q=L->length;q>=1;q--){t=(t+m-1)%q;printf("\n");printf("\t%d\t",L->data[t]);for(int j=t+1;j<=q-1;j++)L->data[j-1]=L->data[j];}printf("\n");}}void main(){Sqlist *s;InitList(s);int a[MAXSIZE];int n=0;printf("请键入要输入几个数"); printf("\n");scanf("%d",&n);for(int i=0;i<n;i++)3 《算法与数据结构》试验报告计算机科学与工程学院{a[i]=i+1;}CreatList(s,a,n);josephus(s);DestoryList(s);printf("\n");}四、测试用例(尽量覆盖所有分支) 1.当输入1,2,3,4。
约瑟夫环——递推公式
0123456789 12345678910 4567891012 789101245 10124578 4578101
810145
45810
1045
104
4约瑟夫环——递推公式
递推公式:
f(N,M)=(f(N−1,M)+M)%N
f(N,M)表⽰,N个⼈报数,每报到M时杀掉那个⼈,最终
f(N−1,M)表⽰,N-1个⼈报数,每报到M时杀掉那个⼈,最终胜利者的编号
现在假设有10个⼈,报到3的⼈就会被杀掉,我们⽤数字给这⼗个⼈编号为
1 2 3 4 5 6 7 8 9 10
第·⼀⾏绿⾊那⾏是数组下标,第⼆⾏是每个⼈的编号
现在逆向推导
f(1,3):只剩最后⼀个⼈,胜利者的数组下标为0
f(2,3)=(f(1,3)+3)%2=1,只有两个⼈的时候,胜利者下标为1。
f(10,3)=3,因为我们数组下标是从0开始的,所以⼈的编号是下标+1,也就是4
那么这个公式是怎么推导的呢?
1.假设我们已经知道了10个⼈时,胜利者的下标为3,那下⼀次9个⼈时,胜利者的下标为多少?
其实就是10个⼈时杀掉了编号为3(即数组下标为2)的⼈后,后⾯的⼈都往前移动了3位,所以胜利者的下标由3变成了0
2.那我们倒过来我们知道9个⼈时,胜利者的下标为0,那10个⼈时胜利者的下标为多少?
其实这和上⾯的问题⼀样,这是这是上个问题的逆过程,就是把⼤家都往后移动3位,所以f(10,3)=f(9,3)+3,不过可能会出现数组越界所以要取模变成f(10,3)=(f(9,3)+3)%10
3.那么⼈数改为n报到m时就杀掉数组怎么移动呢
⼀样的,杀⼀⼈则后⾯的⼈的下标都往前移动m则,f(n,m)=(f(n-1,m)+m)%n。
约瑟夫环问题实验报告
//报数为m的人出列
while(n--)
{
for(int s=m-1; s--; r=p, p = p->link);
cout << "The output is: " << p->data << endl;
r->link = p->link;
LinkList d = new LNode;
if(!d)
二、实验问题描述
设编号为1,2,···,n的n个人围坐一圈,约定编号为k(1≤k≤n)的人从1开始报数,数到m的那个人出列,他的下一位又从1开始报数,数到m的那个人又出列,依次类推,直到所有人出列为止,由此产生一个出队编号的序列。
3、实验步骤
1、实验问题分析
①由于当某个人退出圆圈后,报数的工作要从下一个人开始继续,剩下的人仍要是围成一个圆圈,可以使用循环表;由于退出圆圈的工作对应着表中结点的删除操作,对于这种删除操作频繁的情况,应该选用效率较高的链表结构;为了程序指针每一次都指向一个具体的代表一个人的结点而不需要进行判断,链表不带表头结点。所以,对于所有人围成的圆圈所对对应的数据结构采用一个不带头结点的循环链表来描述。设头指针为p,并根据具体情况移动
可以采用数据类型定义: Typedef struct node {
int number;
struct node *next; }Lnode,*Linklist;
②为了记录退出的人的先后顺序,采用一个顺序表进行存储,程序结束后再输入依次退出的人的编号顺序。由于只记录各个结点的number值就可以,所以定义一个整型一维数组。如“int quite[N];”N为一个根据实际问题定义的一个足够大的整数。
数据结构实验报告-线性表
1 线性表1. 实验题目与环境1.1实验题目及要求(1)顺序表的操作利用顺序存储方式实现下列功能:根据键盘输入数据建立一个线性表,并输出该线性表;对该线性表进行数据的插入、删除、查找操作,并在插入和删除数据后,再输出线性表。
(2)单链表的操作利用链式存储方式实现下列功能:根据键盘输入数据建立一个线性表,并输出该线性表;对该线性表进行数据的插入、删除、查找操作,并在插入和删除数据后,再输出线性表。
(3)线性表的应用约瑟夫环问题。
有n个人围坐一圈,现从某个人开始报数,数到M的人出列,接着从出列的下一个人开始重新报数,数到M的人又出列,如此下去,直到所有人都出列为止。
要求依次输出出列人的编码。
2.问题分析(1)顺序表的操作利用一位数组来描述顺序表,即将所有元素一词储存在数组的连续单元中,要在表头或中间插入一个新元素时,需要将其后的所有元素都向后移动一个位置来为新元素腾出空间。
同理,删除开头或中间的元素时,则将其后的所有元素向前移动一个位置以填补空位。
查找元素时,则需要利用循环语句,一一判断直到找出所要查找的元素(或元素的位置),输出相关内容即可(2)单链表的操作利用若干个结点建立一个链表,每个节点有两个域,即存放元素的数据域和存放指向下一个结点的指针域。
设定一个头指针。
在带头结点的单链表中的第i个元素之前插入一新元素,需要计数找到第i-1个结点并由一指针p指向它,再造一个由一指针s指向的结点,数据为x,并使x的指针域指向第i个结点,最后修改第i-1个结点的指针域,指向x结点。
删除第i个元素时,需要计数寻找到第i个结点,并使指针p指向其前驱结点,然后删除第i个结点并释放被删除结点的空间。
查找第i个元素,需从第一个结点开始计数找到第i个结点,然后输出该结点的数据元素。
(3)线性表的应用程序运行之后,首先要求用户指定初始报数的上限值,可以n<=30,此题中循环链表可不设头结点,而且必须注意空表和"非空表"的界限。
线性表实验报告
LinkList p;
p=L->next;
while(p){
printf("%5d",p->data);
p=p->next;
}
return OK;
}
void main()
{
int i,n,k,d,e;
LinkList La,Lb;
InitList_L(La);
InitList_L(Lb);
e=L.elem[i];
for(j=i;j<=L.length;j++)
L.elem[j]=L.elem[j+1];
L.length--;
return ok;
}
int output(sqlist &L){
int i;
printf("output sqlist data:\n");
for(i=0;i<L.length;i++)
PrintList(La);
printf("\nAfter delete the list is:\n");
ListDelete_L(La,e);
PrintList(La);
printf("\n");
printf("I will insert:");
scanf("%d",&k);
ListInsert_L(La,k);
int a,j,i,m;
CLinkList p,r;
printf("Input the m(m<=20):\nm=");
scanf("%d",&m);
NOIP算法:约瑟夫问题(C++)
约瑟夫问题也称为约瑟夫斯置换,在计算机编程的算法中,类似问题又称为约瑟夫环,又称“丢手绢问题”。
一、一般形式1.N个人围成一圈,从第一个开始报数,第M个将被杀掉,最后剩下一个,其余人都将被杀掉。
例如N=6,M=5,被杀掉的顺序是:5,4,6,2,3,1。
2.有n只猴子,按顺时针方向围成一圈选大王(编号从1到n),从第1号开始报数,一直数到m,数到m的猴子退出圈外,剩下的猴子再接着从1开始报数。
就这样,直到圈内只剩下一只猴子时,这个猴子就是猴王,编程求输入n,m后,输出最后猴王的编号。
算法思路:模拟(1)由于对于每个人只有在与不在环中两种状态(2)开始时每个人都在环中(3)模拟过程,直到环中只剩下一人。
方法一:设立标记,每个元素标记为出队或在队中/ch0302/1748/#include<iostream>#include<algorithm>#include<cstring>#include<cstdio>using namespace std;const int maxn=310;int a[maxn];int main(){int p,num,cnt,n,m,tmp;while(scanf("%d%d",&n,&m)!=EOF&&(n!=0||m!=0)){memset(a,0,sizeof(a));p=1;for(num=1;num<=n;num++){//一共要数n轮for(cnt=1;cnt<=m;cnt++){//每一轮数到mwhile(a[p]==1)p=p%n+1;//如果指针所指已有标记指针后移tmp=p;//此时p指向环中的第cnt只猴子p=p%n+1;//指针后移}a[tmp]=1; //将第m只猴子标记}printf("%d\n",tmp);//第n轮的第m只猴子就是解}return 0;}方法二:使用数组模拟链表codevs1282 约瑟夫问题/problem/1282/#include<iostream>#include<algorithm>#include<cstring>#include<cstdio>#include<vector>using namespace std;const int maxn=30050;int a[maxn];int main(){int n,m,i,j,p;scanf("%d%d",&n,&m);for(i=1;i<n;i++)a[i]=i+1;a[n]=1;p=n;for(i=1;i<=n;i++){for(j=1;j<m;j++)p=a[p];printf("%d ",a[p]);a[p]=a[a[p]];}return 0;}二、深入探讨对于约瑟夫问题,若需要依次记录退出编号的情况:优化方法是使用线段树维护区间和,并进行单点更新。
约瑟夫问题(算法与数据结构课设报告)
线性表的操作及其应用一、问题描述线性表、队列是一种常用的数据结构,有顺序和链式两种存储结构,在实际中应用十分广泛,而链表又分为单链表和循环链表,队列又分为链式队列和循环队列。
这些数据结构都可用来解决约瑟夫环问题。
约瑟夫环问题是算法设计中的一个经典问题,是顺序编号的一组n个人围坐一圈,从第1个人按一定方向顺序报数,在报到m时该人出列,然后按相同方法继续报数,直到所有人出列。
设计算法求约瑟夫环中人员的出列顺序。
二、基本要求1、选择合适的存储结构,建立线性表;2、利用顺序存储结构求解约瑟夫环问题;3、利用单链表和循环链表分别求解约瑟夫环问题;4、利用队列求解约瑟夫环问题。
三、测试数据约瑟夫环的测试数据为7,报数为1至3。
四、算法思想由于用到四种不同的存储结构,它们的算法思想依次是:1、首先建立一个顺序表模拟整个约瑟夫环,手动输入顺序表长(即参加约瑟夫循环的人数)和循环的次数和表元素。
用已经输出总人数和顺序表长作比较,作为外层循环条件。
并对每一个输出后的元素重新赋值以为标记。
对于每次循环,首先检查顺序表此次是不是我们设立的标记,如果不是则循环次数加1,当达到要求的循环次数时就将循环次数设置为0,输出该元素到屏幕并将总输出元素加1。
每次外循环我们都会移到表的下一个位置,作为新的判断条件,每次报到表尾的时候,我们都将重新设置到表尾,作为下次循环的表元素。
2、首先采用链式循环链表建立整个约瑟夫环,手动输入第一次的循环次数和每个人所持下一个循环次数。
设立判断指针指向表头,并将该指针是否为空作为外层循环条件。
做一个内层循环,将判断指针移动到循环要输出的数,并设立一个前指针指向该指针的前一个位置,输出该元素后,将循环次数重新赋值成该元素。
接着判断前指针和判断指针比较,如果相等说明整个表已经输出完毕,否则将删除该位置的元素。
3、用链式队列建立循环约瑟夫环,手动输入人数,第一次的循环次数和每个人所持下一个循环次数。
并将每一个元素依次入队列,根据第一次循环次数,建立一个for循环,每一次循环都出队列,如果达到要求的循环次数就输出,否则进队列,这样这个数字就出现在队尾。
Josephus环问题
Josephus环问题约瑟夫环问题问题描述:Josephus问题可以描述为如下的⼀个游戏:N个⼈编号从1到N,围坐成⼀个圆圈,从1号开始传递⼀个热⼟⾖,经过M次传递后拿着⼟⾖的⼈离开圈⼦,由坐在离开的⼈的后⾯的⼈拿起热⼟⾖继续进⾏游戏,直到圈⼦只剩下最后⼀个⼈。
例如:M=0,N=5,则游戏⼈依次被清除,5号最后留下;如果M=1,N=5,那么被清除的⼈的顺序是2,4,1,5,最后剩下的是3号。
如下是两种解题⽅法:建⽴⼀个N⼤⼩的数组,存储N个⼈是否还在圈⼦内(0为在圈⼦内,-1为已经离开圈⼦),依次循环遍历整个数组,直到剩下的⼈数(left)为1。
public static void pass(int m, int n){int[] num = new int[n];int left = n; //剩下的⼈数int index = 0; //当前遍历到的位置while(left != 1){for(int i = 0; i< m; i++){if(num[index++] != 0) //如果当前⼈已经清除i--;if(index >= n)index = 0;}while(num[index] != 0){index++;if(index >= n)index = 0;}System.out.println("out number is " + (index + 1));num[index] = -1; //将清除的数据下标置-1index++;if(index >= n)index = 0;left--;}}第⼆种⽅式,将1~N的数添加到⼀个ArrayList对列中,⾸先计算偏移量(mPrime),当mPrime⼩于对列长度的⼀半时,则从前往后依次遍历找到清除的元素;当mPrime⼤于对列长度的⼀半时,从后往前遍历找到清除的元素。
public static void pass2(int m, int n){ArrayList<Integer> list = new ArrayList<Integer>();for(int i = 1; i <= n; i++)list.add(i);ListIterator<Integer> iter = list.listIterator();int left = n; //剩下的⼈数int item = 0; //the out numberfor(int i= 1; i < n; i++){int mPrime = m % left;if(mPrime < left/2){ //如果当前的偏移量⼩于list长度的⼀半时if(iter.hasNext())item = iter.next();for(int j = 0; j < mPrime; j++){if(!iter.hasNext())iter= list.listIterator();item = iter.next();}}else{ //当偏移量⼤于list长度的⼀半时,从后往前找for(int j = 0; j< left - mPrime; j++){if(!iter.hasPrevious())iter = list.listIterator(list.size());item = iter.previous();}}System.out.println("out number is " + item);iter.remove();if(!iter.hasNext()) //有可能下⼀次循环mPrime就会⼩于left的⼀半iter = list.listIterator();left--;}}总结当M,N较⼤时,则第⼆种⽅法时间效率更⾼,实现表明,当N=100000,M=9000时(省略的两个算法中的syso语句),⽅法⼀个执⾏时间是30713ms,⽽第⼆种⽅法的执⾏时间仅为4891ms,M越⼤时⽅法⼀的时间效率会更差。
用顺序表解决约瑟夫环问题
⽤顺序表解决约瑟夫环问题⼀、实验题⽬:约瑟夫环问题:设编号为1,2,3,……,n的n(n>0)个⼈按顺时针⽅向围坐⼀圈,m为任意⼀个正整数。
从第⼀个⼈开始顺时针⽅向⾃1起顺序报数,报到m时停⽌并且报m的⼈出列,再从他的下⼀个⼈开始重新从1报数,报到m时停⽌并且报m的⼈出列。
如此下去,直到所有⼈全部出列为⽌。
要求设计⼀个程序模拟此过程,对任意给定的m和n,求出出列编号序列。
实验要求:⽤顺序表实现。
⼆、设计分析:⾸先创建了⼀个顺序表,并且⽤数组存每个⼈的编号,出列的时候将此编号置为0,代表此位置的⼈已经出列,循环查询编号不为0的元素,并⽤变量j记下当前是第⼏个⼈,当j==m的时候,代表此位置是第m个⼈,输出并将编号置为0,⽤k记录出列⼈的下⼀位的索引,并将数组长度-1,再进⾏新⼀轮的循环,直到所有⼈都出列。
三、程序代码:#include<iostream>#include<cstdlib>using namespace std;const int MaxSize=100;typedef struct Node{int data[MaxSize];int length;}List;void CreateList(List *&L,int n){L=(List *)malloc(sizeof(List*));for(int i=1;i<=n;i++){L->data[i]=i;}L->length=n;}void DispList(List *&L,int m){int len=L->length;int j=0,k=1;while(L->length){for(int i=1;i<=len;i++){if(L->data[k]){j++;}if(j==m){cout<<L->data[k]<<" ";L->data[k]=0;L->length--;j=0;k=(k+1)%(len+1)?(k+1)%(len+1):1;break;}k=(k+1)%(len+1)?(k+1)%(len+1):1;}}}int main(){int m,n;cin>>m>>n;List *L;CreateList(L,n);DispList(L,m);return 0;}。
数据结构实验报告一-约瑟夫环问题
实验1约瑟夫环问题1.需求分析(1)输入的形式和输入值的范围:每一次输入的值为两个正整数,中间用逗号隔开。
若分别设为n,m,则输入格式为:“n,m”。
不对非法输入做处理,即假设输入都是合法的。
(2)输出的形式:输出格式1:在字符界面上输出这n个数的输出序列输出格式2:将这n个数的输出序列写入到文件中(3)程序所能达到的功能:对于输入的约瑟夫环长度n和间隔m,输出约瑟夫环的出列顺序。
(4)测试数据:包括正确的输入及其输出结果和含有错误的输入及其输出结果。
正确:输入:10,3输出:3 6 9 2 7 1 8 5 10 4输入:41,3输出:3 6 9 12 15 18 21 24 27 30 33 36 39 1 5 10 14 19 23 28 32 37 41 7 13 20 2634 40 8 17 29 38 11 25 2 22 4 35 16 31错误:输入:10 3输出:6 8 7 1 3 4 2 9 5 102.概要设计(1)抽象数据类型的定义:为实现上述程序的功能,可以用整数存储用户的输入。
并将用户输入的值存储于线性表中。
线性表ADT定义如下:ADT list数据对象:整形数据关系:线性关系,即<ai,ai+1>(0≤a<n)。
基本操作:bool remove(int &elem)//移除一个元素,被移除的元素赋给elem//如果操作成功,返回true,否则返回falsebool isEmpty()//判断数组的元素是否清空,空返回true,否则返回falsebool setPos(int place)//设置当前元素的位置,设置成功返回true,否则返回falseint getLength()//获取数组的实际长度(2)算法的基本思想:约瑟夫环问题中的数据是人所在的位置,而这种数据是存在“第一元素、最后元素”,并且存在“唯一的前驱和后继的”,符合线性表的特点。
约瑟夫环问题的两种解法(循环链表和公式法)
约瑟夫环问题的两种解法(循环链表和公式法)问题描述这⾥是数据结构课堂上的描述:N people form a circle, eliminate a person every k people, who is the final survior?Label each person with 0, 1, 2, ..., n - 1, denote(表⽰,指代) J(n, k) the labels of surviors when there are n people.(J(n, k)表⽰了当有 n 个⼈时幸存者的标号)First eliminate the person labeled k - 1, relabel the rest, starting with 0 for the one originally labeled k.0 1 2 3 ... k-2 k-1 k k+1 ... n-1... k-2 0 1 ...Dynamic programmingJ(n, k) = J(J(n - 1, k) + k) % n, if n > 1,J(1, k) = 0⽤中⽂的⽅式简单翻译⼀下就是 (吐槽:为啥课上不直接⽤中⽂呢?淦!) 有 n 个⼈围成⼀圈,从第⼀个⼈开始,从 1 开始报数,报 k 的⼈就将被杀死,然后从下⼀个⼈开始重新从 1 开始报数,往后还是报 k 的⼈被杀掉,杀到最后只剩⼀个⼈时,其⼈就为幸存者。
(上⾯的英⽂是从 0 开始的,是因为我们写程序时使⽤了数组,所以下标从 0 开始)解决⽅案循环链表⽅法算法思路很简单,我们这⾥使⽤了循环链表模拟了这个过程:节点 1 指向节点 2,节点 2 指向节点 3,...,然后节点 N 再指向节点 1,这样就形成了⼀个圆环。
如图所⽰,n 取 12,k 取 3,从 1 开始报数,然后依次删除 3, 6, 9, 12:#include<stdio.h>#include<stdlib.h>typedef struct Node // 节点存放⼀个数据和指向下⼀个节点的指针{int data;struct Node *next;} *NList; // NList为指向 Node 节点的指针// 创建⼀个节点数为 n 的循环链表NList createList(int n){// 先创建⼀个节点NList p, tmp, head;p = (NList)malloc(sizeof(struct Node));head = p; // 保存头节点p->data = 1; // 第⼀个节点for (int i = 2; i <=n ; i++){tmp = (NList)malloc(sizeof(struct Node));tmp->data = i;p->next = tmp;p = tmp;}p->next = head; // 最后⼀个节点指回开头return head;}// 从编号为 1 的⼈开始报数,报到 k 的⼈出列,被杀掉void processList(NList head, int k){if (!head) return;NList p = head;NList tmp;while (p->next != p){for (int i = 0; i < k - 1; i++){tmp = p;p = p->next;}printf("%d 号被杀死\n", p->data);tmp->next = p->next;free(p);p = NULL; // 防⽌产⽣野指针,下同p = tmp->next;}printf("幸存者为 %d 号", p->data);free(p);p = NULL;}int main(){NList head = createList(11);processList(head, 3);return 0;}测试结果:易知,这个算法的时间复杂度为O(nk),显然,这不是⼀个好的算法。
约瑟夫环顺序表
#include<stdio.h>#include<iostream>using namespace std;template< typename T >class ArrayStack{private:int m_size; //栈容量int m_top; //栈顶,初始为-1T* m_stack; //存放栈内容的连续空间public:ArrayStack(int size);ArrayStack();~ArrayStack();void clear();bool push(const T item);bool pop( T & item);bool top( T & item );};template< typename T >ArrayStack< T >::ArrayStack(int size){this->m_size = size;this->m_top = -1;m_stack = new T[m_size];}template< typename T >ArrayStack< T >::ArrayStack(){this->m_size = 0;this->m_top = -1;m_stack = new T[m_size];}template< typename T >ArrayStack< T >::~ArrayStack(){delete[] m_stack;}template< typename T >void ArrayStack< T >::clear(){this->m_top = -1;}template< typename T >//自动解决栈溢出的入栈操作bool ArrayStack< T >::push(const T item){//扩展存储空间if(this->m_top == m_size - 1){T* newst = new T [this->m_size* 2+1];for(int i = 0 ; i <= this->m_top ; i ++ ){newst[i] = this->m_stack[i];}delete [] this->m_stack;m_stack = newst;m_size = m_size * 2+1;}++ this->m_top;m_stack[this->m_top] = item;return true;}template< typename T >bool ArrayStack< T >::pop( T & item ){if( this->m_top == -1){std::cerr<<"stack is empty, no item can bepopped!"<<std::endl;return false;}else{item = m_stack[this->m_top];this->m_top--;return true;}}template< typename T >bool ArrayStack< T >::top( T & item ){if( this->m_top == -1){std::cerr<<"stack is empty, no item can be read!"<<std::endl;return false;}else{item = m_stack[this->m_top];return true;}}template<typename T>void exchange(char inorder[],char post[],int& m)。
- 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
- 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
- 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。
针后移
} p[head]=p[test]; // 报数到则test所指的位置出列 ,修改head
test:下标值。本次报数完之后,下一个 值为本单元中存放的值,即下一个 test =
P[test]
#include <stdio.h>// main(){
int i, p[17],test, head; // 设置循环控制变量、数组及两个数组下标指针 for(i=0;i<16;i++
p[i]=i+1; //存放下一个单元的下标值(位置号) p[16]=0; test=0; //起始位置(从哪开始报数) while(test!=p[test]){ // 位置号和该位置的下一位置相同时退出
指针指向的下一位置
test=p[head]; //记住出列位置
} printf(″\n%5d″,test); // 最后一个出列位置
}
(位置号) i + 1,即 P[ i ] = i + 1;到最后一个人的时候就循 环到第一个人,设置P[16] =0。
3
P[i] 1 2 3 4 5 6 7 8
0
i
01 2 34 5 6 7
16
head:下标值。由于报到3的倍数的 人要退出,为了保持循环顺序表的形 式,要改变退出的人的前一个的值, 例如:要改变P[1]的值使之变为3。 head用来标识报数的前一个人的下标 值,当报到3的倍数的人退出时,改 变前一个人的单元值为报到3的倍数 的人的单元值,使之保持循环顺序表 的形式。如此下去,当只有一个人的 时候test=p[test]。
❖ 例:设计一个程序求约瑟夫环的出列顺序。约瑟夫 (Joseph)问题:有17个人按顺时针方向围坐一周 (编号为0~16),从第0号的人开始从1报数,凡报到 3的倍数的人离开圈子,一直数下去,直到最后只剩下 一个人为止,问此人原来的位置是多少号?
❖ 算法设计:可设置一个数组P[17];
17个人,用数组下标表示编号,即0~16; 用P[ i ] 值表示某个人,P[ i ]单元存放下一个单元的下标值