约瑟夫环问题

UU们可能都不知道约瑟夫环的问题,我介绍一下下。

介绍

约琴夫环问题约瑟夫环问题由古罗马史学家约瑟夫提出。他参加并记录了公元66一70年犹太人反抗罗马的起义。约瑟夫作为一个将军,设法守住裘达伯特城达47天之久。在城市沦陷之后,他和40名死硬的将士在附近的一个洞穴置难。在那里,这些起义者表决说”要投降毋宁死”、于是,约瑟夫建议每个人轮流杀死他旁边的人,而这个顺序是由抽签决定的。约瑟夫有预谋地抓到了最后一签,并且,作为洞穴中的两个幸存者之一,他说服了他原先的牺牲品一起投降了罗马。

emmm,就是这样,嘿嘿嘿嘿,开始将题目思路吧。

我们先设人数为5,循环数为3.开始作图: 

这需要一个循环的结构,在大一就学习了数组,老是想不出咋循环,用的是计数器,然后靠%的去余数的方法也能做,但是今天我们说一下链式结构咋做。

如果我们使用链表的话,那就得考虑:

  • 是否有头结点,毕竟头结点也会进入循环计数范围,所以说,是不需要头结点的。

  • 需要几个指针,他们应该在那个位置?我做的是两个指针pre和p,他们一起移动,p负责寻找删除点,pre是在某点删除后把链表连起来的作用。

伪代码

注意初始化,还删除结点时,p指向pre的后继结点

C++代码

我们先看一下题目,按其格式打一下代码,其实无论题目如何说,其实主要问你的是人数和报的数。

我们要做的就是,设结点和三个函数的代码,分别是单链表的析构函数、构造函数、循环函数。

#include
using namespace std;
​
struct Node{
    int data;
    Node *next;
};
​
class Heng
{
    public:
        Heng(int n);
        ~Heng( );
        void Chi(int m);
    private:
        Node *rear;
}

构造函数要完成的事情,就是做一个循环单链表,了解这个目的,而且每一个数据域是连续的排到规定人数。按我之前介绍的构造单链表,是有两种方法,头插法和尾插法,每一种方法数据进入单链表的顺序都不一样,但是头插法我不会做,毕竟每有头结点,如果你做出了,我们可以一起讨论。

我真的很想知道用头插法咋设计。

Heng::Heng(int n){
    Node *s;
    s = NULL;
    rear =new Node;
    rear->data = 1;
    rear->next =rear;
    for(int i =2;i<n+1;i++){
        s =new Node;
        s->data = i;
        s->next =rear->next;
        rear->next = s;
        rear = s;
    }
}Heng::Heng(int n){
    Node *s;
    s = NULL;
    rear =new Node;
    rear->data = 1;
    rear->next =rear;
    for(int i =2;i<n+1;i++){
        s =new Node;
        s->data = i;
        s->next =rear->next;
        rear->next = s;
        rear = s;
    }
}

单链表设计完之后,需要做的是做游戏咯。按循环排除人,就需要计数器,count,还有我们之前说的两个工作指针。(count取余数为0,是我用的方法,此段代码是书中代码,讲的比我清楚)就到需要删除的数了。这时候需要pre保存之前p后面next的数据,所以需要两个指针。

Heng::Chi(int m){
Node *pre = rear, *p = rear->next; //初始化工作指针p和pre
int count = 1;
cout << "出环的顺序是:";
while (p->next != p) //循环直到循环链表中只剩一个结点
{
if (count < m) { //计数器未累加到密码值
pre = p; p = p->next; //将工作指针pre和p分别后移
count++;
}
else { //计数器已经累加到密码值
cout << p->data<< " "; //输出出环的编号
pre->next = p->next; //将结点p摘链
delete p;
p = pre->next; //工作指针p后移,但pre不动
count = 1; //计数器从1开始重新计数
}
}
cout << p->data << " "; //输出最后一个结点的编号
delete p; //释放最后一个结点
rear = nullptr;

}

最后一个是析构函数,就是释放空间的作用,我觉着就不用说咯。

一定要写析构函数哦,不然会有报错:

 我之前创造类的时候,如果你细心,你会发现我在后面没有打“;”,这个也会报错哦!

完整代码(和上面有些不同):

#include <iostream> 
using namespace std;

struct Node 
{
  int data;
  Node *next;
};

class Heng
{
public:
   Heng(int n)
{
   Node *s = NULL;
   rear = new Node;
   rear->data = 1; rear->next = rear; 
   for (int i = 2; i <= n; i++) 
{
   s = new Node; s->data = i;
   s->next = rear->next;
   rear->next = s;
   rear = s; 
}
}

   void Chi(int m)
{
   Node *a = rear, *b = rear->next; 
   int num = 1;
   while (b->next != b) 
{
   if (num % m!=0) { //之前这打错了,抱歉哈。
   a = b; b = b->next; 
   num++;
}
else { 
   cout << b->data<< " ";
    a->next = b->next; 
   delete b;
   b = a->next; 
   num ++; 
}
}
   cout << b->data << " "; 
   delete b; 
   rear = NULL;
}
   ~Heng( )
{
   if (rear != NULL){
   Node *p = rear->next;
   while(rear->next != rear)
{
   rear->next = p->next;
   delete p;
   p = rear->next;
}
delete rear;
}
}
   private:
   Node *rear;
};

int main( )
{
   int x, y;
  cin >> x;
   cin >> y;
   Heng R(x);
   R.Chi(y);
return 0;
}

 OK,这次分享结束咯。

今天补一个错误,之前打代码没有注意,吧声明的num打错成count,现在改动了,感谢粉丝指正。大家一起加油(σ゚∀゚)σ..:*☆。

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值