【数据结构题集(c语言版)】约瑟夫环 题解(循环链表+模拟+循环)

约瑟夫环

题目描述

约瑟夫(Joseph)问题的一种描述是:编号为1,2,…,n的n个人按顺时针方向围坐一圈,每人持有一个密码(正整数)。一开始任选一个正整数作为报数上限值m,从第一个人开始按顺时针方向自1开始顺序报数,报到m时停止报数。报的人出列,将他的密码作为新的m值,从他在顺时针方向上的下一个人开始重新从1报数,如此下去,直至所有人全部出列为止。试设计一个程序求出出列顺序。

基本要求

利用单向循环链表存储结构模拟此过程,按照出列的顺序印出各人的编号。

测试数据

m的初值为20;n=7,7个人的密码依次为:3,1,7,2,4,8,4,首先m值为6(正确的出列顺序应为6,1,4,7,2,3,5)。

实现提示

程序运行后,首先要求用户指定初始报数上限值,然后读取各人的密码。可设n≤30。此题所用的循环链表中不需要“头结点”,请注意空表和非空表的界限。

选作内容

向上述程序中添加在顺序结构上实现的部分。


思路

首先定义一个指针 p 指向 rear。然后进入一个循环,在循环中,首先进行报数,报数的次数是当前的上限值 m。具体的操作是,p 指针沿着链表向前移动 m-1 次,然后 p 指向的节点的下一个节点就是需要移除的节点。

定义一个临时指针 tmp 指向 p 的下一个节点(需要移除的节点),然后输出 tmpid,并将 tmpdata(密码)赋值给 m 作为新的报数上限值。接着,将 pnext 指针指向 tmp 的下一个节点,这样就实现了移除 tmp 节点的操作。最后,释放 tmp 节点的内存。

循环继续进行,直到链表中只剩下一个节点,这时 p 的下一个节点就是 p 本身,输出 pid 并结束。


代码

#include <algorithm>
#include <iostream>
#define AUTHOR "HEX9CF"
using namespace std;
using Status = int;
using ElemType = int;

const int N = 1e6 + 7;
const int TRUE = 1;
const int FALSE = 0;
const int OK = 1;
const int ERROR = 0;
const int INFEASIBLE = -1;
// const int OVERFLOW = -2;

int n, m;
ElemType a[N];

struct ListNode {
	int id;
	ElemType data;
	ListNode *next;
};
using LinkedList = ListNode *;

Status initList(LinkedList &rear) {
	rear = NULL;
	return OK;
}

Status pushBack(LinkedList &rear, int id, ElemType data) {
	ListNode *newNode = (ListNode *)malloc(sizeof(ListNode));
	if (!newNode) {
		return ERROR;
	}
	newNode->id = id;
	newNode->data = data;
	if (rear) {
		newNode->next = rear->next;
		rear->next = newNode;
		rear = newNode;
	} else {
		newNode->next = newNode;
		rear = newNode;
	}
	return OK;
}

Status display(LinkedList rear) {
	if (!rear) {
		return ERROR;
	}
	ListNode *p = rear->next;
	do {
		cout << p->data << " ";
		p = p->next;
	} while (p != rear->next);
	cout << endl;
	return OK;
}

void solve(LinkedList &rear, int m) {
	ListNode *p = rear;
	do {
		for (int i = 1; i < m && p; i++) {
			p = p->next;
		}
		ListNode *tmp = p->next;
		cout << tmp->id << " ";
		m = tmp->data;
		p->next = tmp->next;
		free(tmp);
	} while (p->next != p);
	cout << p->id << "\n";
}

int main() {
	cin >> n >> m;
	for (int i = 0; i < n; i++) {
		cin >> a[i];
	}

	LinkedList rear;
	initList(rear);
	for (int i = 0; i < n; i++) {
		pushBack(rear, i + 1, a[i]);
	}

	// display(rear);
	solve(rear, m);
	return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值