约瑟夫环问题--不同存储结构对应的不同解法

 一、题目描述

约瑟夫环问题是一个很经典的问题:有M个人,编号分别为1到M,玩约瑟夫环游戏,最初时按编号顺序排成队列;每遍游戏开始时,有一个正整数报数密码N,队列中人依次围坐成一圈,从队首的人开始报数,报到N的人出列,然后再从出列的下一人开始重新报数,报到N的人出列;重复这一过程,直至所有人出列,完成一遍游戏,所有出列的人形成新队列;游戏可能玩很多遍,每遍有新报数密码。求若干遍游戏完成后队列次序。本题要求使用单链表实现,程序要求采用模块化设计,格式规范,有合适注解。

例题链接


 二、逻辑结构

这道题的思维很简单,无非是记录一些人,再出去一人,然后进行循环,直到所有人出去。可知逻辑关系是线性的,可采用顺序或链式存储结构。笔者在接触这道题时是以前学习单链表时,所以自然而然地想到可以用单链表解决。此外还可以采用队列,将前m-1个人出队再入队,pop第m个人,再次循环。下面来看一下针对单链表和队列的不同处理方式。


三、不同存储结构

1.链式存储

设计一个循环单链表,不需要头节点,但需要头指针 first 用来遍历。头指针作为全局变量。初始时获取人的数量num,由num建立单链表,各节点编号从1到num。

为了实现要求的“游戏可以玩多次”的功能,设计一个函数:参数为头指针、人数、报数密码。“依次遍历n-1次, 再将第n个出队到一个临时数组中”,对上述操作循环,直至所有人出队,在利用临时数组构建新的循环单链表,将头指针设为 first 。

函数功能实现完后,依次将每轮游戏的报数密码传入函数。所有轮游戏结束后再遍历输出答案即可。

#include<iostream>
#include<iomanip>
using namespace std;

struct Node
{
	int val;
	Node* next;
	Node():val(0),next(NULL){}
	Node(int v):val(v),next(NULL){}
};

Node* first;      //头指针

Node* fun(Node* first, int n,int num)        //约瑟夫问题实现函数
{ 
	int* ans = new int[num];
	int time = 0;
	if (n == 1)                  //报数密码为1时出队顺序就是原链表,无需执行
		return first;

	Node* q = new Node;
	while (time < num)           //需出队num次
	{
		Node* cur = first;
		for (int i = 1; i < n - 1; i++)
		{
			cur = cur->next;            //遍历完后,删除cur的下一个
		}
		ans[time] = cur->next->val;
		q = cur->next;
		cur->next = q->next;
		first = cur->next;         //一个人出队后,将头指针设为出队人的下一个再循环
		time++;
	}
	Node* new_head = new Node(ans[0]);
	Node* new_tail = new_head;
	for (int i = 1; i < num; i++)
	{
		Node* tmp = new Node(ans[i]);
		new_tail->next = tmp;
		new_tail = tmp;
	}
	new_tail->next = new_head;
	return new_head;
}

int main()
{
	int num;
	cin >> num;


	first = new Node(1);
	Node* tail = first;
	for (int i = 2; i <= num; i++)    //先创建一个单链表
	{
		Node* tmp = new Node(i);
		tail->next = tmp;
		tail = tmp;
	}
	tail->next = first; 


	int n;
	while (cin >> n)
	{
		first=fun(first, n, num);      //玩多轮游戏
	}
	

	for (int i = 1; i <= num; i++)
	{
		cout << first->val << setw(4);
		first = first->next;
	}
}

2.队列

队列做存储结构的话更为简单,“出队第m个人要做的是将前m-1个人不断出队再入队,将第m个人放到临时数组中”,对上述操作循环直至将所有人出队完。之后再将临时数组放回队列就完成一次游戏。

#include<iostream>
#include<queue>
#include<iomanip>
void fun(queue<int>& win, int n, int num)
{
	int* a = new int[num];
	int i = 0;
	while (i < num) {
		int tmp = 0;
		for (int i = 1; i <= n - 1; i++)
		{
			tmp = win.front();
			win.pop();
			win.push(tmp);
		}
		a[i++] = win.front();
		win.pop();
	}

	for (int i = 0; i < num; i++)
	{
		win.push(a[i]);
	}
}

int main()
{
	int num;
	cin >> num;
	cin.ignore();
	queue<int>win;
	for (int i = 1; i <= num; i++)
	{
		win.push(i);
	}
	int n;
	while (cin >> n)
	{
		fun(win, n, num);
	}
	for (int i = 0; i < num; i++)
	{
		cout << win.front() << setw(4);
		win.pop();
	}
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值