约瑟夫环问题(Josephus problem)循环链表解决

【问题描述】

    n 个人(编号从1~n)围成一圈,从第 k 个人开始数数,数到 m 的人出圈,然后继续从未出列的下一个人开始数数,数到 m 的人出圈,重复上述过程,直到圈中仅剩下一人。

【输入形式】

    输入为一行三个正整数,n、k、m。

【输出形式】

    输出为一个正整数,表示最后剩下的人的编号。

【样例输入】

100 1 5
【样例输出】

47
【答题提醒】本题为程序片段题,你需要将程序补充完整。题目为类与对象的应用,构造一个循环链表,你需要完全理解本程序的设计思想。
实现思路和注释仅供自己理解,不一定正确

#include  <iostream>
using  namespace  std;
class  person//看做person节点 
{
private:
        int  no;            //人的编号
        person  *next;      //指向相邻的下一个人
public:
        person(int  num)
        {
                no=num;
                next=NULL;//初始化next指针为空指针 
        }
        void  setNext(person  *p)//方便next指针存放person类指针 
        {
                next=p;
        }
        int  getNo()
        {
                return  no;
        }
        person  *getNext()
        {
                return  next;
        }
};

class  cycle
{
private:
        person  *start;       //开始数数的位置
        int  out;             //数到几出列
        int  inQueue;         //队伍中现有人数
public:
        cycle(int  num,  int  from,  int  whoOut)//num对应n,from对应k,whoout对应m 
        {
                inQueue=num,out=whoOut;//赋值,把n赋值给inQueue,把m赋值给out
                person *prv=NULL,*first=NULL;//指针初始化,都设为空指针,pre是previous,之前的意思,这里指上一个 
                for(int  i=1;i<=num;i++)
                {person  *p=new  person(i);//动态开辟内存 
                if  (i==1)//当i是1时 
                first=p;//把头节点等价于p 
                if  (i==from)//当i是k时 
                start=p;//开始数数的节点等价于p 
                if  (i>1)//当i大于1时 
                prv->setNext(p);//类外调用成员函数,prv指针的next指针等价于p 
                prv=p;//prv可看作一个移动的大箭头,不断在遍历链表 
                }
                prv->setNext(first);//当完整循环完后,把头指针再存到最后一个prv的next指针,从而使得循环成立,简而言之,链表变成了一个圈 
        }
        int  getInQueue()
        {
        return  inQueue;//得到队伍的人数 
        }
        
        void  cnt()//根据题目要求数数,确定出列的人,将该人从圈中剔除
        {person *q,*temp;
        q=start;//先初始化,由于该函数是起剔除作用,所以一上来就让q等于start 
		temp=NULL;//初始化为空指针 
		for(int i=1;i<out;i++){//在到第m人出列之前 
		temp=q;//储存q的信息 
		q=q->getNext();//q自身返回next指针,即遍历链表中	
		}
		//遍历到out前那个人之后 
		if(out==1){//如果第一人就是被淘汰的人 
		start=q->getNext();//start指针就是第一个人的next指针,就是指向下一个人,方便继续遍历 
		inQueue--;//少了一个人,所以圈内人数减一 
		delete q;//这个人被淘汰了,所以释放这个节点的内存,这就解释了为什么释放之前要把这个人的next指针传给start指针 
		}
        else{temp->setNext(q->getNext());//这行代码很关键,out前一个人q里的next指针存在了temp里的next指针,如果没有这个储存,链表会断掉
		//原因是线性结构遭到破坏,上一个节点不能找到指向下一个节点的指针,上面out=1的情况,由于是第一个节点都直接被删除,所以不需要保存指向下一个节点的指针 
            start=q->getNext();//同上 
            inQueue--;//同上 
		delete q;//同上 
		}
}
        person  *getStart()
        {
        return  start;
    }
        ~cycle()
        {
        delete  start;//析构并释放头节点 
        }
};

int  main()
{       int  n,k,m;//n是一开始圈有多少人    k是从第k人开始数数  m是数到第m人出列  
        cin>>n>>k>>m;
        cycle  *p=new  cycle(n,k,m);
        while(p->getInQueue()>1)//当队伍剩余人数仍不为1时,调用cnt函数,剔除无关人员 
        p->cnt();
        person  *winer=p->getStart(); //剔除完后,令指针winer得到start指针(都是person类) 
        cout<<winer->getNo()<<endl;//调用得到编号的成员函数,输出剩下的人的成员编号 
        delete  p;
        return  0;
}

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值