使用STL库list类实现单双向约瑟夫环问题(C++)

目录

一、单向约瑟夫环 

1.问题描述

2.list类函数用法  

(1)list构造

(2)list iterator迭代器

(3)list容量

(4)list元素访问

(5)list增删查改

(6)list算法操作

3.代码演示

4.结果演示 

 

二、双向约瑟夫环

1.问题描述

2.代码演示

3.结果演示


一、单向约瑟夫环 

1.问题描述

       约瑟夫生者死者游戏的大意是:30个旅客同乘一条船,因为严重超载,加上风高浪大,危险万分;因此船长告诉乘客,只有将全船一半的旅客投入海中,其余人才能幸免遇难。无奈,大家只得同意这种办法,并议定30个人围成一圈,由第一个人开始,依次报数,数到第9人,便把他投入大海中,然后从他的下一个人数起,数到第9人,再将他投入大海,如此循环,直到剩下15个乘客为止。问哪些位置是将被扔下大海的位置。

       为了解决这一问题,可以用长度为30的数组作为线性存储结构,并把该数组看成是一个首尾相接的环形结构,那么每投入大海一个乘客,就要在该数组的相应位置做一个删除标记,该单元以后就不再作为计数单元。这样做不仅算法较为复杂,而且效率低,还要移动大量的元素。用单循环链表解决这一问题,实现的方法相对要简单得多。首先要定义链表结点,单循环链表的结点结构与一般的结点结构完全相同,只是数据域用一个整数来表示位置;然后将它们组成具有30个结点的单循环链表。接下来从位置为1的结点开始数,数到第8个结点,就将下一个结点从循环链表中删去,然后再从删去结点的下一个结点开始数起,数到第8个结点,再将其下一个结点删去,如此进行下去,直到剩下15个结点为止。

       为了不失一般性,将30改为一个任意输入的正整数n,而报数上限(原为9)也为一个任选的正整数m。并且我们知道C++中有STL库可以调用list类进行链表的各种操作,所以代码就能变得很精炼。下面先来介绍一下list类的函数用法。

 

2.list类函数用法  

(1)list构造

构造函数 接口说明
list() 构造空的list
list (size_type n, const value_type& val = value_type())构造的list中包含n个值为val的元素
list (const list& x) 拷贝构造函数
list (InputIterator first, InputIterator last) 

用[first, last)区间中的元素构造list

 

(2)list iterator迭代器

   这里的迭代器理解成一个指针,该指针指向list中的某个节点。

函数声明接口类型
begin()返回第一个元素的迭代器
end()返回最后一个元素下一个元素的迭代器
rbegin()返回第一个元素的reverse_iterator,即end位置
rend()返回最后一个元素下一个位置的reverse_iterator,即begin位置

(3)list容量

函数声明接口说明
empty()检测list是否为空,是返回true,否则返回false
size()返回list中有效节点的个数

(4)list元素访问

函数申明接口说明
front()返回list的第一个节点中值的引用
back()返回list最后一个节点中值的引用

(5)list增删查改

函数声明 接口说明
push_back() 尾插
push_front() 头插
pop_back()尾删
pop_front()头删
insert()  在pos位置插入值为val的元素
erase() 删除pos位置的元素
swap()交换两个元素
clear() 清空list中的有效元素

(6)list算法操作

函数声明接口说明
remove()删除指定值的元素
reverse()反转链表
unique()去重
merge()合并两个有序链表
sort()链表排序

   

3.代码演示

#include<iostream>
#include<list>      //list容器的头文件
using namespace std;

int main()
{	
	int m, n, count=1;//计数器 
	
	cout<<"请输入总人数n\n";
	cin >> n;
	cout<<"请输入数到m退出的数m\n";
	cin >> m;
	
	list <int> l;//创建链表
	for(int i=1;i<=n;i++)
	{
		l.push_back(i);
	}//对n个元素标号
	
	list<int>::iterator iter = l.begin();  //设置一个迭代器指向头部
	
	cout << "船上的人的编号为:" << endl;
	
	for(iter = l.begin(); iter != l.end(); iter++)
	{
		cout << *iter << " ";
	}
	cout << endl;
		
	iter = l.begin();   //让迭代器指向第一个元素
	while(l.size() > (n/2))//链表人数如果等于一半人,那么删除操作结束
	{
		for(int i = 0 ; i < m-1; i++) //迭代器位移m-1次,表示数到m 
		{
			if(iter == --l.end())//如果迭代器指向了链表最后一个元素,即l.end()的前一位,则返回到第一个节点处 
			{
				iter = l.begin();
			}
			else
			{ 
				iter++;//如果迭代器没有指向最后一个元素,那么就正常自增
			}
		}
		
		cout << "第" << count << "次离开的人的序号是" << *iter << endl;
			
		if(iter != --l.end())      //如果不是最后一个元素,正常删除即可 
		{
			iter = l.erase(iter); //erase()函数返回的是删除元素下一位,这里是防止下一位是end()的情况
		}
		else
	   	{
	   		l.pop_back();  //如果是最后一个元素的话,直接调用pop_back函数尾删,并且迭代器直接返回到链表第一个节点 
			iter = l.begin();
		}	
			
		
		count++;
	}
		
		cout << "船上还剩下的人为:" << endl;
		
		for(iter = l.begin(); iter != l.end(); iter++)
		{
			cout << *iter << " ";
		}
		cout << endl;
		
		
	return 0;
 } 


4.结果演示 

 


二、双向约瑟夫环

1.问题描述

       约瑟夫双向生死游戏是在约瑟夫生者死者游戏的基础上,正向计数后反向计数,然后再正向计数。具体描述如下:30个旅客同乘一条船,因为严重超载,加上风高浪大,危险万分;因此船长告诉乘客,只有将全船一半的旅客投入海中,其余人才能幸免遇难。无奈,大家只得同意这种办法,并议定30个人围成一圈,由第一个人开始,顺时针依次报数,数到第9人,便把他投入大海中,然后从他的下一个人数起,逆时针数到第5人,将他投入大海,然后从他逆时针的下一个人数起,顺时针数到第9人,再将他投入大海,如此循环,直到剩下15个乘客为止。问哪些位置是将被扔下大海的位置。

       本游戏的数学建模如下:假设n个旅客排成一个环形,依次顺序编号1,2,…,n。从某个指定的第1号开始,沿环计数,数到第m个人就让其出列,然后从第m+1个人反向计数到m-k+1个人,让其出列,然后从m-k个人开始重新正向沿环计数,再数m个人后让其出列,然后再反向数k 个人后让其出列。这个过程一直进行到剩下q个旅客为止。

本游戏的要求用户输入的内容包括:

1. 旅客的个数,也就是n的值;

2. 正向离开旅客的间隔数,也就是m的值;

3. 反向离开旅客的间隔数,也就是k的值;

4. 所有旅客的序号作为一组数据要求存放在某种数据结构中。

本游戏要求输出的内容是包括

1. 离开旅客的序号;

2. 剩余旅客的序号;

所以,根据上面的模型分析及输入输出参数分析,可以定义一种数据结构后进行算法实现。

       约瑟夫双向生死游戏如果用单循环链表作为线性存储结构,就只能正向计数结点,反向计数比较困难,算法较为复杂,而且效率低。用双向循环链表解决这一问题,实现的方法相对要简单得多。

为了不失一般性,将30改为一个任意输入的正整数n,而正向报数上限(原为9)也为一个任选的正整数m,正向报数上限(原为5)也为一个任选的正整数k。

 

 

2.代码演示

#include<iostream>
#include<list>//list容器的头文件
using namespace std;
int main()
{	
	int m, n, k, count=1;//计数器 
	
	cout<<"请输入总人数n\n";
	cin >> n;
	cout<<"请输入顺时针报数的数m:\n";
	cin >> m;
	cout<<"请输入逆时针报数的数k:\n";
	cin >> k;
	
	list <int> l;//创建链表
	for(int i=1;i<=n;i++)
	{
		l.push_back(i);
	}//对n个元素标号
	
	list<int>::iterator iter = l.begin();  //设置一个迭代器指向头部

	
	cout << "船上的人的序号为:" << endl;
	
	for(iter = l.begin(); iter != l.end(); iter++)
	{
		cout << *iter << " ";
	}
	cout << endl;
	
	iter = l.begin();      //让迭代器指向第一个元素 
	while(l.size()  >  (n / 2))//链表人数如果等于一半人,那么删除操作结束
	{
		for(int i = 0 ; i < m-1; i++) //迭代器向后位移m-1次,表示数到m 
		{
			if(iter == --l.end())//如果迭代器指向了链表最后一个元素,即l.end()的前一位,则返回到第一个节点处 
			{
				iter = l.begin();
			}
			else
			{ 
				iter++;//如果迭代器没有指向最后一个元素,那么就正常自增
			}
		}
		iter++;  //顺时针数完从他的后一个开始逆时针报数 
		
		for(int i = 0 ; i < k-1; i++) //迭代器向前位移k-1次,表示数到k 
		{
			if(iter == l.begin())//如果迭代器指向了链表的头结点,即l.begin,则返回到尾节点处 
			{
				iter = --l.end();
			}
			else
			{ 
				iter--;//如果迭代器没有指向头结点,那么就正常自减 
			}
		}
		
		cout << "第" << count << "次离开的人的序号是" << *iter << endl;
			
		if(iter != --l.end())              //如果不是最后一个元素,正常删除即可 
		{
			iter = l.erase(iter);        //erase()函数返回的是删除元素下一位,这里是防止下一位是end()的情况
			iter--;						//(删除完后下一轮要从前一个元素开始正向报数)
		}
		else
	   	{
	   		l.pop_back();        //如果是最后一个元素的话,直接调用pop_back函数尾删,并且迭代器直接返回到链表尾节点
			iter = l.end();      //(删除完后下一轮要从前一个元素开始正向报数) 	
		}	
		
		count++;
	}
		
		cout << "船上还剩下的人为:" << endl;
		
		for(iter = l.begin(); iter != l.end(); iter++)
		{
			cout << *iter << " ";
		}
		cout << endl;
	
	
	return 0;
 } 

3.结果演示

 

  • 4
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
STL(Standard Template Library)是C++中的一个标准,其中包含了许多常用的数据结构和算法。其中,list是一种常用的数据结构,它是一个双向链表,可以实现快速的插入和删除操作。 下面是C++STL list的简实现: ```cpp template <typename T> class List { private: struct Node { T data; Node* prev; Node* next; Node(const T& d = T(), Node* p = nullptr, Node* n = nullptr) : data(d), prev(p), next(n) {} }; Node* head; Node* tail; int size; public: List() : head(nullptr), tail(nullptr), size(0) {} List(const List& other) : head(nullptr), tail(nullptr), size(0) { for (const auto& item : other) { push_back(item); } } ~List() { clear(); } List& operator=(const List& other) { if (this != &other) { List tmp(other); swap(tmp); } return *this; } void push_back(const T& item) { Node* new_node = new Node(item, tail, nullptr); if (tail) { tail->next = new_node; } else { head = new_node; } tail = new_node; ++size; } void push_front(const T& item) { Node* new_node = new Node(item, nullptr, head); if (head) { head->prev = new_node; } else { tail = new_node; } head = new_node; ++size; } void pop_back() { if (tail) { Node* tmp = tail; tail = tail->prev; if (tail) { tail->next = nullptr; } else { head = nullptr; } delete tmp; --size; } } void pop_front() { if (head) { Node* tmp = head; head = head->next; if (head) { head->prev = nullptr; } else { tail = nullptr; } delete tmp; --size; } } T& front() { return head->data; } T& back() { return tail->data; } bool empty() const { return size == 0; } int size() const { return size; } void clear() { while (!empty()) { pop_back(); } } class iterator { private: Node* ptr; public: iterator(Node* p = nullptr) : ptr(p) {} iterator(const iterator& other) : ptr(other.ptr) {} iterator& operator++() { ptr = ptr->next; return *this; } iterator operator++(int) { iterator tmp(*this); ptr = ptr->next; return tmp; } iterator& operator--() { ptr = ptr->prev; return *this; } iterator operator--(int) { iterator tmp(*this); ptr = ptr->prev; return tmp; } T& operator*() const { return ptr->data; } T* operator->() const { return &(ptr->data); } bool operator==(const iterator& other) const { return ptr == other.ptr; } bool operator!=(const iterator& other) const { return ptr != other.ptr; } }; iterator begin() const { return iterator(head); } iterator end() const { return iterator(nullptr); } iterator rbegin() const { return iterator(tail); } iterator rend() const { return iterator(nullptr); } void swap(List& other) { std::swap(head, other.head); std::swap(tail, other.tail); std::swap(size, other.size); } }; ``` 上述代码中,List中定义了一个私有结构体Node,它表示双向链表中的每个节点。Node中包含了数据成员data、指向前一个节点的指针prev和指向后一个节点的指针next。List中还包含了头指针head、尾指针tail和大小size等数据成员。 List提供了许多操作,包括push_back、push_front、pop_back、pop_front、front、back、empty、size、clear等。其中,push_back和push_front分别表示在双向链表的末尾和开头插入一个元素,pop_back和pop_front分别表示删除双向链表的末尾和开头的元素,front和back分别表示获取双向链表的第一个和最后一个元素,empty表示判断双向链表是否为空,size表示获取双向链表中元素的个数,clear表示清空双向链表中的所有元素。 List还定义了一个迭代器iterator,它可以用于遍历双向链表中的所有元素。迭代器中包含了一个指向Node的指针ptr,它可以指向双向链表中的任意一个节点。迭代器提供了许多操作,包括++、--、*、->、==、!=等。其中,++和--分别表示迭代器向前和向后移动一个位置,*表示获取迭代器所指向节点的数据成员data,->表示获取迭代器所指向节点的数据成员data的指针,==和!=分别表示判断两个迭代器是否相等和不相等。 List中还提供了一些其他操作,比如拷贝构造函数、析构函数、赋值运算符、begin、end、rbegin、rend和swap等。其中,拷贝构造函数用于复制另一个List对象,析构函数用于释放所有节点的内存,赋值运算符用于将一个List对象赋值给另一个List对象,begin和end分别返回指向双向链表中第一个元素和最后一个元素的迭代器,rbegin和rend分别返回指向双向链表中最后一个元素和第一个元素的迭代器,swap用于交换两个List对象中的所有数据成员。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值