数据结构——线性表:约瑟夫问题

约瑟夫问题:

编号为1,2,…,n的n个人按顺时针方向围坐一圈,每人持有一个密码(正整数)。一开始任选一个正整数作为报数上限值m,从第一个人开始按顺时针方向自1开始顺序报数,报到m时停止报数。报m的人出列,将他的密码作为新的m值,从他在顺时针方向上的下一个人开始重新从1报数,如此下去,直至所有人全部出列为止。试设计一个程序,按照出列的顺序打印出各人的编号。

问题分析

数据对象:处理一组带密码和编号的人的集合
实现功能:依次接收键盘输入的整数,以线性表的形式存储到计算机内存中,实现按顺时针依次访问数据并删除数据功能,同时要输出被删除数据的编号,直到线性表为空表的时候终止访问。
结果展示:通过屏幕输出每次循环中的结果,即一组编号集合

举例

输入数据:
elem:二元组(密码D1,编号D2)
{(3,1),(1,2),(7,3),(2,4),(4,5),(8,6),(4,7)}
即键盘键入:
7 (初始的密码值通过随机数函数产生)
3 1 7 2 4 8 4
从第一个开始,等到第六个删除,集合变为{(3,1),(1,2),(7,3),(2,4),(4,5),(4,7)},m为8,再从1开始数到m,输出数到的那个数(即6),并把那个数对应的密码值返回,在下一个操作中作为新的循环次数当成函数实参输入到函数中,在重复执行n次后,直到线性表中数据元素的个数为0,线性表成为空表,跳出循环
输出结果:
屏幕输出:
6 1 4 7 2 3 5

算法

抽象数据类型:
要访问一组二元数据,并删除,用线性表
数据对象:两组整数,分别是密码值和编号
数据关系:由于通过键盘依次输入密码值,数据单元的编号由输入的次序决定,故整个数据单元满足线性特征,所以呈线性关系,可以通过线性表来表示。
基本操作:准备能够存储两组整数的存储空间,添加整形元素,遍历空间,获取元素值,删除指定数据
算法设计:

	ADT integerset{
		数据对象:一组elem,每个单元包含两个元素:
D={ (ai, bi) | ai, bi ∈ 整数,i=1,2,,n, n>0}
		数据关系:R={<ai-1, ai>&&<bi-1, bi> | ai-1, ai∈D1, bi-1, bi∈D2 }
		基本操作:(功能描述:包括插入<添加>,遍历,删除,查找。要挑选)
			Void init{}//用来插入元素
			Bool append{const Elem&}//用来在队尾加入元素
			Void setstart{}//用来回到开头
			Void getvalue{Elem&}//用来得到数据集的数据对象的值
			Void getlistlength{}//用来得到当前线性表的长度
			Bool find{const Elem}//用来查找数据集为某个值的元素的位置
			Void remove{}//删除某个元素
	}

算法实现

输入模块:
先设计两个整数类型用来输入n一个整数,再设计一个把输入的整数保存为线性表的函数void input{list&l},完成一个集合的输入,存在线性表la中
计算&输出模块:
设计一个循环的函数,void xunhuan{list& la , int n},通过for循环实现多次调用。
将线性表和循环次数次数作为函数形参,每次执行函数得到的elem中的编号值(bi)输出到屏幕上,新建立一个整型的变量将数据单元中的密码值(ai)保存下来,再将当前的单元通过调用remove函数删除,将新建立的整型变量作为函数返回值,函数结束。
在下一次循环操作中,将上次函数返回的整型变量作为新的函数实参(即新的循环次数),线性表仍作为实参不变,继续调用函数。
直到for循环执行了n次,线性表中所有元素全部被删除,线性表此时已经是一个空表之后,终止循环。
输出模块:
屏幕上显示每次循环返回的参数
物理数据结构的实现:
物理数据类型:两个整型变量,分别代表elem中的密码和编号,即
struct elem{//数据集
int code; //密码
int num; //编号
}
采用链表实现,因为通过指针的强指向性便于定义和表示数据之间的线性关系

算法步骤

1、从键盘读入线性表中数据的个数n
2、通过调用Void input(list &l);函数,将从键盘键入的一系列数据添加到建立的线性表中

Void input(list <int> &l){
	elem it;
	While (cin>>it.num>>it.code;){
		la.append(it);//输入数据值
	}
}

3、处理与输出:
从线性表la中开始n次循环,每次循环中调用Void xunhuan(list& la , int n);函数进行操作,在其中将执行Void next();函数(用来向后移动)、Void getvalue()函数(用来得到当前符合条件位置的数据对象的值)、Void remove()函数(运来执行线性表中数据单元的删除),在每次xunhuan()函数运行中将一个数据单元的序号值输出到屏幕上。知道线性表中所有的数据单元均输出、删除,即线性表已经成为一个空表,跳出for循环。

int xunhuan(list<int>&la, int m){
if(la.getlistlength()==0){//如果已经是没有元素的空表了,跳出递归
break;
}
For(int i=0;i<m;i++){
If(la.next()==NULL){//如果是尾部,则回到头部
	La.setstart();
}else{//如果不是尾部,则向后移一个元素
	La.next();
}
	}
elem mm=la.getvalue();//得到编号
	Cout<<mm.num<<” “;//输出编号
La.remove();//删除当前元素 
return mm.code;
}
for循环:
for(int i=0;i<n&&la.getlistlength()!=0;i++){
m=xunhuan(a, m);
}

复杂度分析

输入模块:主要执行函数append(),由于函数中有一个for循环,for循环中只存在输入操作,故时间复杂度为O(n),模块的时间复杂度为O(n)
计算,输出模块:主要执行for循环,在for循环中调用函数xunhuan(),单独函数运行过程中会执行m次操作,m的值与输入的n个二元数据中的的code数据值有关,与数据对象的大小无关,所以函数的时间复杂度函数为Σ(i=0→n)ci,故最终模块的时间复杂度为O(n)

已标记关键词 清除标记
相关推荐
©️2020 CSDN 皮肤主题: 撸撸猫 设计师:C马雯娟 返回首页