1.三种解法的异同
如果说前两种方法功能相同,实现方法不同,是因为在数据的存储结构上,第一种方法是采用了一维动态数组,第二种方法是采用了单循环链表存储结构,致使尽管最后执行结果是相同的,但写出的语句代码却是有很大的差别,但是前两者的共同点是都属于面向过程的编程方法。
第三种约瑟夫环的解法,除了存储结构不同,因为这种方法在内存中申请连续的空间,有多少个小朋友,就申请了多少个数据结点的连续空间,但每个数据的后继结点是由每个结点的指针域来描述的,是在连续空间上的链式存储,因此说存储结构上是不同的。另外,在编程方式上也与前两种方法不一样,这种方法是面向对象的编程方法。
C++语言之所以让学生感到复杂,是因为它有四种编程范式,分别为 面向过程、 面向对象、 泛型编程和函数编程(Lambda 表达式实现),属于技术覆盖非常全面的一种编程语言。
2.两个类的设计
面向对象的编程方法,类的设计如下:
小孩结构体
{
小孩编号
后面小孩的地址
}
小孩的循环链队类(ChiledredRingLink)——约瑟夫环
{
数据成员:
链队队头指针
当前小孩指针
当前小孩前一个小孩的指针(因为当前小孩出队,需要前一个小孩的地址)
函数:
构造函数
{
按小孩个数申请空间
把所有的小孩初始化为一个单循环链队
给三个数据成员赋值
}
析构函数 { 释放构造函数申请的空间 }
当前指针指向到指定位置m的指针移动函数
{
for (i=1;i当前数据指针和它的前驱指针两个指针一起移动
}
小孩出队函数 { 让当前指针的前驱指针,指向当前指针下一个小孩 当前指针后移一个 }
获取当前小孩编号的函数
{ 返回当前指针的数据域的值,这个值是小孩的序号
}
输出所有小孩
{
按每行6个小孩,输出所有的孩子
}
}
循环链队应用类
{ 数据成员:
小孩人数
间隔值
开始位置
函数:
构造函数
{
数据有效性验证
对3个数据成员赋值
}
查找胜利者函数
{
创建一个循环链队
当前指针指向起始位置
for(int i=1;i{ 到指定间隔值位置
输出离队小孩序号
小孩离队
}
最后队列中的一个小孩儿是胜利者
}
3.实现代码
#include using namespace std;struct Child{int data; // 小孩序号
Child *next; //指向下一个小孩的指针
};//小孩的结构定义class ChildRingLink
{public:
ChildRingLink(int n)//构造函数 n个小孩的循环链表
{if(n<2) throw exception();
pHead=new Child[n];//申请了一个数组,数组中每个元素是Child结构体类型for(int i=1;i<=n;i++){
pHead[i-1].next=&pHead[i%n];//把n个数组元素链接成一个循环链表
pHead[i-1].data=i;//从第一个结点的数据域,开始编序号为1-n
}
p_pri=&pHead[n-1];//指向最后一个结点;
p_cur=pHead ;//指向第一结点;因为是循环链表,p_pri是p_cur的前一个结点
}void countBy(int m)//p_pri和p_cur两指针一起向后移动m个位置{for(int i=1;i//让p_cur指针指向第m个位置
{
p_pri=p_pri->next;//前面指针后移
p_cur=p_cur->next;//当前指针后移
}
}
int getChildIndex() const//取出当前指针中的数据,也就是小孩儿的序号{
return p_cur->data;
}
void childLeave()//p_cur指针指向的结点脱离链表{
p_pri-> next=p_cur->next; //前面指针连接当前指针后面的结点
p_cur=p_cur->next;//当前指针后移一个结点
}
void outputAll() const{
int numinline=0;//计数器
Child * p=pHead;// 定义一个指针指向链表的第一个结点
while(p->next!=pHead)//循环链表没有结束
{
cout<data<<"\t";//输出数据域
p=p->next; //p指针后移
numinline++;//计数器加1if(numinline%6==0)//每行输出个数据cout<<endl;
}cout<<endl;
}
~ChildRingLink()//析构函数
{delete [] pHead;//释放数组占用的空间
}private:
Child *pHead,*p_pri,*p_cur;//头指针,当前数据指针前一指针,当前数据指针
};//约瑟夫环类的定义class ChildRingLinkApp//应用类
{public:
ChildRingLinkApp(int n,int m,int s=1);//构造函数说明,起始位置设置默认值1void creatLink_getWinner() ;//创建孩子队列,并找出胜利者private:int childrenNum;//孩子人数int start;//起始位置int interval;//间隔数,从起始位置开始,第interval个孩子离开队伍
};
ChildRingLinkApp::ChildRingLinkApp(int n,int m,int s)//应用类构造函数的定义
{//对应用类的3个数据成员进行初始化
childrenNum=n;
interval=m;
start=s;//对数据的有效性进行验证if(childrenNum<2||interval<1||interval>n||start<1||start>n)
{cerr <<"人数、间隔值或起始位置有无效数据\n";throw exception();
}
}void ChildRingLinkApp::creatLink_getWinner() {cout<<"共有"<"个小孩."<<endl;//创建队伍ChildRingLink childrenQueue(childrenNum);cout<<"小孩的编号如下:"<<endl;//输出全部队列上的结点的函数
childrenQueue.outputAll();//当前指针去起始位置
childrenQueue.countBy(start);cout<<"离开队伍的小孩的序号顺序为:"<<endl;int count=0;//计数器,每行输出6个数据for(int i=1;i {
childrenQueue.countBy(interval);//到间隔值位置cout<"\t";
count++;//计数器加1if(count%6==0)cout<<endl;//六个后换行//childrenQueue中第intervar位置的小孩出队
childrenQueue.childLeave();
}cout <<endl;cout<<"最后胜利的小朋友是"<"号"<<endl;
}int main(){ int n,m,s;//人数、间隔值、起始位置cout<<"请输入人数、间隔值和起始位置:";cin>>n>>m>>s;ChildRingLinkApp childQ(n,m,s);//创建一个约瑟夫环的应用对象
childQ.creatLink_getWinner(); //调用获取胜利者函数
system("pause");return 1;
}
4.测试结果
(1)输入 41 3 1 (共41个小孩,从第1个孩子开始报数,报到3的孩子出队,然后下一个再次从1开始报数,报到3的出队,循环反复,最后一个出队的小孩第第31号小朋友)
![da9905b5d0af21d77f95cd30c15da202.png](https://i-blog.csdnimg.cn/blog_migrate/78a3155fa349e8e818036cbebf48399b.png)
![5c9d1a733bf62262411809cd235ecf8c.png](https://i-blog.csdnimg.cn/blog_migrate/bc9a2acf6b5f7c710ba09052797be708.jpeg)
(2)输入 10 3 1
![1772d6637ac061089f34660b03beeaf5.png](https://i-blog.csdnimg.cn/blog_migrate/eb4e42093176684536185c65114ab51e.png)
感谢关注!