循环单向链表与约瑟夫问题

news2024/11/28 10:57:22

循环链表介绍

先不急着看约瑟夫问题是什么,先了解循环链表的结构,那什么是循环链表?

循环,顾名思义,从链表中第一个节点出发,还会遇到第一个节点,形成循环的一环。也就是说链表中最后一个节点的下一个节点是第一个节点,但是头节点也是一个节点,所以最后一个节点的下一个节点应该是头节点才对。

如下图,有两种情况:

其中更大的长方形是节点的数据域,更小的长方形是节点的指针域,指向链表中的下一个节点。

循环链表的代码实现

因为解决约瑟夫问题,只需要初始化、插入、打印功能,所以别的功能就没实现。

节点定义

首先,循环链表和普通链表节点的结构体定义一模一样,那就写出来吧。

typedef struct _LoopLinkList //循环链表
{
	int date;
	struct _LoopLinkList* next;
}LinkNode,LinkList;

 循环链表的初始化

初始化循环链表,其中函数的参数是一个指向头节点的指针,如图所示:

两个箭头有些重叠了,不过无伤大雅。

函数参数中的取地址符 & 是引用的意思,为C++特有的,相当于就在使用这个一级指针 L ,无需使用二级指针来接收这个一级指针 L 的地址,如果去掉这个 & ,会发生什么事呢?那就是 L 这个指针所指向的地址无法改变,但是可以改变所指向的变量的值。

bool initLinkList(LinkList *&L)
{
	L = new LinkNode;     //为头结点申请空间
	if (!L) return false; // L为空指针,生成头结点失败

	L->next = L; //头结点指向自己
	L->date = -1;
	return true;
}

如果上面那段话不太明白,可以先跳过,等写完所有代码,然后把初始化函数参数中的 & 去掉,在 IDE 中调试一下即可得出答案。

循环链表的插入(尾插法)

其中函数的参数节点指针 node 指向新加节点。

bool InsertBack(LinkList* &L, LinkNode* node)
{
	if (!L || !node) return false; //如果链表头结点未创建或者传入的结点指针指向空

    //分两种情况,一是链表为空,只有头结点存在
	if (L->next == L)
	{
		node->next = L;
		L->next = node;
	}
	else
	{
		LinkNode* p = L;
		while (p->next != L)
		{
			p = p->next;
		} //找到最后一个节点

		p->next = node;
		node->next = L;
	}
	return true;
}

循环链表的打印

从头节点开始打印,直到又为头节点。

bool LinkPrint(LinkList* L)
{
	if (!L) return false;

	LinkNode* p = L;
	p = L ->next;
	while (p != L)
	{
		printf("%d ", p->date);
		p = p->next;
	}
	return true;
}

约瑟夫问题

解决了循环链表,就开始约瑟夫问题了,哈哈,这故事有很多的分支,我之前听过的不是下面这种。

据说著名犹太历史学家Josephus有过以下的故事:在罗马人占领乔塔帕特后,39 个犹太人与Josephus及他的朋友躲到一个洞中,39个犹太人决定宁愿死也不要被敌人抓到,于是决定了一个自杀方式,41个人排成一个圆圈,由第1个人开始报数,每报数到第3人该人就必须自杀,然后再由下一个重新报数,直到所有人都自杀身亡为止。然而Josephus 和他的朋友并不想遵从。首先从一个人开始,越过k-2个人(因为第一个人已经被越过),并杀掉第k个人。接着,再越过k-1个人,并杀掉第k个人。这个过程沿着圆圈一直进行,直到最终只剩下一个人留下,这个人就可以继续活着。问题是,给定了和,一开始要站在什么地方才能避免被处决。Josephus要他的朋友先假装遵从,他将朋友与自己安排在第16个与第31个位置,于是逃过了这场死亡游戏。

——摘自约瑟夫问题_百度百科

 不过看完这个故事应该大概了解了约瑟夫问题,这个问题可以用循环链表解决,每一个人都是链表中一个节点,只要某个节点报数报到出圈的那个号码,就把这个节点从链表中删除。

由于原问题的人数实在太多,我这里就简化一下,一共 10 人,报数报到 9 的人出圈,并且要求求出第五轮出圈的号码,还有最后一个出圈的人的号码,所以添加了两个整型变量 loops、num来记录。

首先在进入这个函数之前已经把 10 个节点插入了链表,第 n 个节点的值为 n ,例如:第一个节点的数据域为 1 。

代码实现

其中函数参数 interval 为间隔,也就是出圈的号码 9 。

bool Joseph(LinkList* &L,int interval)
{
	if ( !L || L->next == L) return false; // 头节点未初始化或者是空链表
	if (interval < 1) return false; //报数的间隔也不能小于 1 , 1 的话还能玩,也就是一报数就死

	LinkNode* p = L , *q = NULL ; // q 指向要删除的节点, p 指向要删除的节点的前一个节点
	int loops = 0 , num = 0 ;     //loops表示进行到第几轮,num保存着出圈的人的号码

	int j = 1;

	while (L->next != L) //不为空链表
	{

		while ( j < interval ) //一直报数,除非j等于出圈的数-1,这时候指针p就指向了要删除的节点的前一个节点
		{
			if (p->next != L)
			{
				j++;
			}	
			p = p->next;
		}
		if (p->next == L) //如果p指向最后一个节点,那肯定不能删除头节点,应该删除第一个节点,所以p赋值为头节点
		{
			p = L;
		}

        //删除
		q = p->next;
		num = q->date;
		p->next = q->next;
        delete q;

		j = 1;
        loops++;

		cout <<endl<< "这是第"<<loops<<"轮:" ;
		LinkPrint(L);
		
		if (loops == 5)
		{
			printf("\n第5轮出圈的是:%d", num);
		}
	}

	printf("最后一个出圈的是:%d\n", num);
	return true;
}

 可能有人会想为什么其中循环的条件不是 i <= j 呢?因为在单向链表中,你删除一个节点前,必须要让删除节点的上一个节点的next 指针指向删除节点的下一个节点。

所以指向要删除的节点的上一个节点就方便很多了

如下图:

完整代码以及运行结果

#define _CRT_SECURE_NO_WARNINGS 1

#include <iostream>

using namespace std;
//结点结构体
typedef struct _LoopLinkList
{
	int date;
	struct _LoopLinkList* next;
}LinkNode,LinkList;

//初始化
bool initLinkList(LinkList* &L)
{ 
	L = new LinkNode;
	if (!L) return false; 

	
	L->next = L; //指向自己
	L->date = -1;
	return true;
}


//尾插法
bool InsertBack(LinkList* L, LinkNode* node)
{
	if (!L || !node) return false; 


	if (L->next == L) //链表为空,只有头结点
	{
		node->next = L;
		L->next = node;
	}
	else
	{
		LinkNode* p = L;
		while (p->next != L)
		{
			p = p->next;
		}
		p->next = node;
		node->next = L;
	}
	return true;
}

//打印
bool LinkPrint(LinkList* L)
{
	if (!L) return false;

	LinkNode* p = L;
	p = L ->next;
	while (p != L)
	{
		printf("%d ", p->date);
		p = p->next;
	}
	return true;
}



//解决joseph问题
bool Joseph(LinkList*& L, int interval)
{
	if (!L || L->next == L) return false; // 头节点未初始化或者为空链表
	if (interval < 1) return false;  //报数的间隔

	LinkNode* p = L, * q = NULL; 
	int loops = 0, num = 0;     //loops表示进行到第几轮,num保存着最后一个出圈的人的号码

	int j = 1;

	while (L->next != L) 
	{

		while (j < interval) 
		{
			if (p->next != L)
			{
				j++;
			}
			p = p->next;
		}
		if (p->next == L) 
		{
			p = L;
		}

		//此时 p 指向要删除节点的前一个节点(不为最后一个节点)
		q = p->next;
		num = q->date;
		p->next = q->next;
		delete q;

		j = 1;
		loops++;

		cout << endl << "这是第" << loops << "轮:";
		LinkPrint(L);

		if (loops == 5)
		{
			printf("\n第5轮出圈的是:%d", num);
		}
	}

	printf("最后一个出圈的是:%d\n", num);
	return true;
}
int main(void)
{
	LinkList* L = NULL ;
	LinkNode* e = NULL;

	//1.初始化循环链表
	initLinkList(L);
	
	//2.尾插循环链表
	for (int i = 1; i <= 10; i++)
	{
		e = new LinkNode;
		e->date = i;
		e->next = NULL;
		InsertBack(L, e);
	}


	//3.打印链表
	LinkPrint(L);

	Joseph(L, 9);
	return 0;
}

---------------------------------------------------------------------------------------------------------------------------------

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1295786.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

项目实战之RabbitMQ冗余双写架构

&#x1f9d1;‍&#x1f4bb;作者名称&#xff1a;DaenCode &#x1f3a4;作者简介&#xff1a;啥技术都喜欢捣鼓捣鼓&#xff0c;喜欢分享技术、经验、生活。 &#x1f60e;人生感悟&#xff1a;尝尽人生百味&#xff0c;方知世间冷暖。 &#x1f4d6;所属专栏&#xff1a;项…

12.6每日一题(备战蓝桥杯程序的控制结构)

12.6每日一题&#xff08;备战蓝桥杯程序的控制结构&#xff09; 题目 1638: 【入门】判断正负数或零题目描述输入输出样例输入样例输出来源/分类 题解 1638: 【入门】判断正负数或零题目 1348: 【入门】求绝对值题目描述输入输出样例输入样例输出来源/分类 题解 1348: 【入门】…

【教学类-35-05】17号的学号字帖(A4竖版1份)

作品展示&#xff1a; 背景需求&#xff1a; 大四班17号男孩目前无法自主数学数字。他表示自己能够认识数字&#xff0c;但不会写。 保育老师说&#xff1a;我曾经教过他&#xff0c;抓着手示范的。但是他记不住。家里估计也不练习的。年龄还没到&#xff0c;下学期再看看能不…

SpringBoot项目访问resources下的静态资源

1.新建一个配置文件夹&#xff0c;放配置类 2.编辑 WebMvcConfig.java package com.southwind.configuration;import org.springframework.context.annotation.Configuration; import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry; import or…

期末速成数据库极简版【查询】(2)

目录 select数据查询----表 【1】筛选列 【2】where简单查询 【3】top-n/distinct/排序的查询 【4】常用内置函数 常用日期函数 常用的字符串函数 【5】模糊查询 【6】表数据操作——增/删/改 插入 更新 删除 【7】数据汇总 聚合 分类 ​ &#x1f642;&#…

2.6 A 的 LU 分解

一、A LU 线性代数很多关键的概念实际上就是矩阵的分解&#xff08;factorization&#xff09;。原始矩阵 A A A 变成两个或三个特殊矩阵的乘积。第一个分解&#xff0c;实际上也是最重要的分解&#xff0c;来自消元法。因子 L L L 和 U U U 都是三角形矩阵&#xff0c;分…

自动化测试:PO模式详解!

PO&#xff08;Page Object&#xff09;模式是一种在自动化测试中常用的设计模式&#xff0c;将页面的每个元素封装成一个对象&#xff0c;通过操作对象来进行页面的交互。 概括来说就是&#xff0c;每个页面都有对应的PO类&#xff0c;PO类中包含了页面的元素定位和操作方法。…

ArkTS语言难吗?鸿蒙指南

HarmonyOS的开发语言是ArkTS、JS(JavaScript)。 ArkTS简介 ArkTS是HarmonyOS优选的主力应用开发语言。ArkTS围绕应用开发在TypeScript&#xff08;简称TS&#xff09;生态基础上做了进一步扩展&#xff0c;继承了TS的所有特性&#xff0c;是TS的超集。因此&#xff0c;在学习…

c语言-动态内存管理

文章目录 一、为什么会有动态内存管理二、申请内存函数1、malloc2、free3、calloc4、realloc 三、常见的动态内存的错误四、练习 一、为什么会有动态内存管理 1.我们一般的开辟空间方式&#xff1a; int a 0;//申请4个字节空间 int arr[10] { 0 };//申请40个字节空间2.这样…

如何运用gpt改写出高质量的文章 (1)

大家好&#xff0c;今天来聊聊如何运用gpt改写出高质量的文章 (1)&#xff0c;希望能给大家提供一点参考。 以下是针对论文重复率高的情况&#xff0c;提供一些修改建议和技巧&#xff1a; 如何运用GPT改写出高质量的文章 一、引言 随着人工智能技术的飞速发展&#xff0c;自然…

QT 重定向qdebug输出到自绘界面

因为在嵌入式中调试qt需要查看输出信息,特意写了一个类用户便捷查看qdebug信息 界面如下: 提供了开始,停止,保存,清空,退出功能,具体代码下文给出 文件如下 #ifndef QDEBUGREDIRECT_H #define QDEBUGREDIRECT_H /**qdebug 重定向类 定向到界面控件*李吉磊 2023.12.7* */#in…

vue2中使用jsplumb完成流程图

前言 之前的几个demo都是在vue3中写的,虽然可以直接拿去复用。 但是根据有些看客反馈,想用一个vue2版本的,毕竟很多人开发功能的时间都不是特别富裕。大多时候还是用现成的demo更好一些。 这里我就写一个简易版本的demo,可以实现绘制,并且删除连接线和节点等功能,篇幅也不大…

麻雀1号开发板开箱

麻雀1号是上海睿赛德电子科技有限公司全新推出的一款高性价比音频Wi-Fi开发板&#xff0c;内置RT-Thread&#xff0c;主打 Wi-Fi、音频和摄像头拍照功能&#xff0c;配合丰富的组件及例程&#xff0c;可降低多媒体应用的开发门槛。 开发板介绍 正面&#xff1a; 背面&#x…

手搓图片滑动验证码_JavaScript进阶

手搓图片滑动验证码 背景代码效果图展示网站 背景 在做前端项目开发的时候&#xff0c;少不了登录注册部分&#xff0c;既然有登录注册就少不了机器人验证&#xff0c;验证的方法有很多种&#xff0c;比如短信验证码、邮箱验证码、图片滑动、图片验证码等。 由于鄙人在开发中…

docker安装与详细配置redis

docker安装redis 连接虚拟机 vagrant up //启动虚拟机 vagrant ssh //连接虚拟机进入root用户 su root输入密码&#xff1a;和账户名一样 vagrant 下载redis 直接下载redis镜像,下载redis最新镜像 docker pull redis下载的都是DockerHub中默认的官方镜像 创建文件目…

golang slice 数组针对某个字段进行排序

这里主要用到golang的sort.Sort方法&#xff0c;先看这个函数的简介&#xff1a; 介绍链接&#xff1a;https://books.studygolang.com/The-Golang-Standard-Library-by-Example/chapter03/03.1.html 如何实现&#xff1a; import "sort"// UserInfo 用户信息结构…

Bionorica成功完成SAP S/4HANA升级 提升医药制造业务效率

企业如何成功地将其现有的ERP ECC系统转换升级到SAP S/4HANA&#xff0c; 并挖掘相关潜力来推动其数字化战略&#xff1f;Bionorica应用SNP软件实施了实时ERP套件&#xff0c;为进一步的增长和未来的创新奠定了基础。 草药市场的领导者&#xff1a;Bionorica Bionorica是世界领…

.NET使用分布式网络爬虫框架DotnetSpider快速开发爬虫功能

前言 前段时间有同学在微信群里提问&#xff0c;要使用.NET开发一个简单的爬虫功能但是没有做过无从下手。今天给大家推荐一个轻量、灵活、高性能、跨平台的分布式网络爬虫框架&#xff08;可以帮助 .NET 工程师快速的完成爬虫的开发&#xff09;&#xff1a;DotnetSpider。 注…

Java实现屏幕截图程序(一)

在Java中&#xff0c;可以使用Robot类来实现屏幕截图程序。Robot类提供了一组用于生成输入事件和控制鼠标和键盘的方法。 Java实现屏幕截图的步骤如下&#xff1a; 导入Robot类 import java.awt.Robot;创建Robot对象 Robot robot new Robot();获取屏幕分辨率信息 Dimensi…

力扣面试题 08.12. 八皇后(java回溯解法)

Problem: 面试题 08.12. 八皇后 文章目录 题目描述思路解题方法复杂度Code 题目描述 思路 八皇后问题的性质可以利用回溯来解决&#xff0c;将大问题具体分解成如下待解决问题&#xff1a; 1.以棋盘的每一行为回溯的决策阶段&#xff0c;判断当前棋盘位置能否放置棋子 2.如何判…