C++面向对象方法求解约瑟夫环问题

约瑟夫问题有很多种解法及其变种,这里的约瑟夫环问题是这样的:
约瑟夫环(约瑟夫问题)是一个数学的应用问题:已知n个人(以编号1,2,3...n分别表示)围坐在一张圆桌周围。从编号为k的人开始报数,数到m的那个人出列;他的下一个人又从1开始报数,数到m的那个人又出列;依此规律重复下去,直到圆桌周围的人全部出列.
大多数的约瑟夫环问题解法是利用面向过程的方法编写的(其实我没有看过多少。这里我给出一个利用C++面向对象的编程思想编写的代码,用于解决上面的约瑟夫环问题。这里的核心算法也是动态链表。
首先阐述一下我的算法思想:

每个参加游戏的人看做是一个链表中的一个结点,结点的内容为:


想像一下n个人围坐成一圈,手拉着手。这里的链表实现也是一样的,将n个结点首尾相连,形成一个环。像这样:


现在假设某一结点是表头,如:


又假设关键数(要数的那个数)m=3,那么第一轮出局的将会是表头下一个的下一个(表头的第三个),如图所示:

要将这个结点踢出局外,也就是将它上一个结点的指向下一个结点的指针指向要出局的结点的下一结点,同时为了保持这个环,还要设置要出局的结点的下一结点的指向上一结点的指针指向要出局的结点的上一结点。如图:

最后不要忘了删除那个不要的结点。对分配在堆上的对象,C++是要手动进行内存管理的。
上面是主要的思想,下面是算法的实现

首先定义结点:

struct _Josephus
{
int data;//当前的数据点
_Josephus* previous;//上一个结点
_Josephus* next;//下一个结点
};

然后定义操作的类
//操作类
class Josephus
{
public:
Josephus() = delete;
Josephus(int count, int number) :Count(count), Number(number){}
~Josephus();
void CreateLink();//创建整个链表,并保存表头在head里。整个链表将构成一个环
void Action();//利用创建好的链表,并返回结果
vector<int> jose;//保存的结果


private:
int Count;//参加游戏的人数
int Number;//关键数字m
_Josephus* head;//表头
ReNode StartAgain(_Josephus*);//传入(新的)开始的结点,返回ReNode并且完成搭桥过程
};
void CreateLink();将用于创建这个链表,并将表头保存在Josephus类的数据成员head中。 函数定义如下:
void Josephus::CreateLink()
{
_Josephus* head=new _Josephus;//表头
_Josephus* next;//下一个结点
_Josephus* curr;//当前结点
head->data = 1;
curr = head;
for (int i = 1; i < this->Count; i++)
{
next = new _Josephus;
curr->next = next;
next->data = i + 1;
next->previous = curr;
curr = next;
}
//将这个链表构成一个环
next->next = head;
head->previous = next;
this->head = head;
}
这个函数应该不难,大家先这样看看吧。
ReNode StartAgain(_Josephus*)是一个主要的函数,大家应该也注意到了前面的返回类型ReNode到底是什么呢?下面我来和大家说说吧。
ReNode的定义如下:
//下面是辅助类
struct ReNode
{
int result;//当前轮的结果
_Josephus* curr;//下一轮开始的结点位置
};
成员result保存的是要出局的那个人的编号,而curr是下一轮要开始的结点。有了这个辅助结构,算法会简单很多。
ReNode StartAgain(_Josephus*)的函数定义如下:
ReNode Josephus::StartAgain(_Josephus* head)
{
ReNode node;
int num=1;//用于循环计数,从1开始
if (head->next == head)//如果当前结点的下一结点是其本身,则游戏结束——因为只有一个结点了
{
//设置ReNode
node.curr = NULL;//这里设置为空是为了退出Action的while循环
node.result = head->data;
}
else
{
/*
过程:
1.进行计数
2.得到ReNode
3.设置当前轮游戏结果的上一结点的下一结点为当前轮游戏结果的下一结点和
 当前结点的下一结点的上一结点为当前结点的上一结点——相当于将这个结点踢出局外,游戏人数将减少一个
*/


//步骤1.进行计数
while (num!=this->Number)
{
head = head->next;
num++;
}//退出while时,head刚好是数到this->Number的那个结点

//步骤2.得到ReNode
//设置ReNode
node.curr = head->next;
node.result = head->data;


//步骤3.
head->previous->next = head->next;
head->next->previous = head->previous;
进行内存管理——释放游离的结点的内存
delete head;
}
return node;
}


最后我们来看看最后一个函数void Josephus::Action();它的作用是将每一轮要出局的人的编号保存在类的jose中,jose是一个[code=c]vectot<int>[/code],用于保存整个游戏的结果,并且开始新一轮的游戏,直到所有人都出局。
函数void Josephus::Action();的定义如下:
void Josephus::Action()
{


ReNode node;
while (this->head!=NULL)
{
node = StartAgain(this->head);//从head处开始游戏,并返回ReNode
this->jose.push_back(node.result);//得到每一轮的游戏结果
this->head = node.curr;//下一轮开始的结点
}
}
到此,整个算法的思想和主要的代码都列出来了。
下面给出完整的实现代码:
在文件中josephus.h
#pragma once
#ifndef __JOSEHPUS__HH
#define __JOSEHPUS__HH
#include<vector>
using std::vector;


//真正的结点
struct _Josephus
{
int data;//当前的数据点
_Josephus* previous;//上一个结点
_Josephus* next;//下一个结点
};


//下面是辅助类
struct ReNode
{
int result;//当前轮的结果
_Josephus* curr;//下一轮开始的结点位置
};


//操作类
class Josephus
{
public:
Josephus() = delete;
Josephus(int count, int number) :Count(count), Number(number){}
~Josephus();
void CreateLink();//创建整个链表,并保存表头在head里。整个链表将构成一个环
void Action();//利用创建好的链表,并返回结果
vector<int> jose;//保存的结果


private:
int Count;
int Number;
_Josephus* head;//表头
ReNode StartAgain(_Josephus*);//传入(新的)开始的结点,返回ReNode并且完成搭桥过程
};
#endif
在文件josephus.cpp中
#include "Josephus.h"

Josephus::~Josephus()
{
delete head;
}
void Josephus::CreateLink()
{
_Josephus* head=new _Josephus;//表头
_Josephus* next;//下一个结点
_Josephus* curr;//当前结点
head->data = 1;
curr = head;
for (int i = 1; i < this->Count; i++)
{
next = new _Josephus;
curr->next = next;
next->data = i + 1;
next->previous = curr;
curr = next;
}
//将这个链表构成一个环
next->next = head;
head->previous = next;
this->head = head;
}


void Josephus::Action()
{


ReNode node;
while (this->head!=NULL)
{
node = StartAgain(this->head);//从head处开始游戏,并返回ReNode
this->jose.push_back(node.result);//得到每一轮的游戏结果
this->head = node.curr;//下一轮开始的结点
}
}
ReNode Josephus::StartAgain(_Josephus* head)
{
ReNode node;
int num=1;//用于循环计数,从1开始
if (head->next == head)//如果当前结点的下一结点是其本身,则游戏结束——因为只有一个结点了
{
//设置ReNode
node.curr = NULL;//这里设置为空是为了退出Action的while循环
node.result = head->data;
}
else
{
/*
过程:
1.进行计数
2.得到ReNode
3.设置当前轮游戏结果的上一结点的下一结点为当前轮游戏结果的下一结点和
 当前结点的下一结点的上一结点为当前结点的上一结点——相当于将这个结点踢出局外,游戏人数将减少一个
*/


//步骤1.进行计数
while (num!=this->Number)
{
head = head->next;
num++;
}//退出while时,head刚好是数到this->Number的那个结点

//步骤2.得到ReNode
//设置ReNode
node.curr = head->next;
node.result = head->data;


//步骤3.
head->previous->next = head->next;
head->next->previous = head->previous;
进行内存管理——释放游离的结点的内存
delete head;
}
return node;
}
最后还有一个用于辅助的头文件,内容如下:

在NHelper.h中

#include<iostream>
#ifndef __NAMESPACEHELPER__HH__
#define __NAMESPACEHELPER__HH__
#define NH__HH using std::cin;using std::cout;using std::endl;
#endif


  • 8
    点赞
  • 31
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 2
    评论
好的,下面是C++面向对象实现约瑟夫环的代码: ```c++ #include <iostream> using namespace std; // 定义一个结点类 class Node { public: int data; Node *next; Node(int d) { data = d; next = NULL; } }; // 定义一个约瑟夫环类 class Josephus { public: int n, m; // n表示总人数,m表示出列的间隔 Node *head; // 队列的头结点 Josephus(int n, int m) { this->n = n; this->m = m; head = NULL; } // 初始化队列 void init() { for (int i = 1; i <= n; i++) { Node *p = new Node(i); if (head == NULL) { head = p; p->next = head; // 队列首尾相连 } else { Node *tail = head; while (tail->next != head) { tail = tail->next; } tail->next = p; p->next = head; // 队列首尾相连 } } } // 出列操作 void out() { Node *p = head, *pre = head; while (n > 1) { for (int i = 0; i < m - 1; i++) { pre = p; p = p->next; } pre->next = p->next; delete p; p = pre->next; n--; } cout << "最后一个出列的人是:" << p->data << endl; } }; int main() { int n, m; cout << "请输入总人数和出列的间隔:"; cin >> n >> m; Josephus j(n, m); j.init(); // 初始化队列 j.out(); // 开始出列操作 return 0; } ``` 解释一下代码: 首先定义了一个结点类 `Node`,用于构建链表;然后定义了约瑟夫环类 `Josephus`,其中包含了总人数 `n`、出列的间隔 `m`、队列的头结点 `head`。类中有两个方法:`init()` 用于初始化队列;`out()` 用于出列操作;main() 函数中创建了一个 `Josephus` 对象,调用 `init()` 和 `out()` 方法,完成约瑟夫环的求解。 具体实现细节可以参考代码注释。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

一尺丈量

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值