c++ 对象起始地址 指针靠齐_约瑟夫环解法之三——(面向对象编程方式)

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
5c9d1a733bf62262411809cd235ecf8c.png
约瑟夫环

(2)输入 10 3 1

1772d6637ac061089f34660b03beeaf5.png

感谢关注!

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值