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

循环链表介绍

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

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

如下图,有两种情况:

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

循环链表的代码实现

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

节点定义

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

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.mzph.cn/news/207710.shtml

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

相关文章

python 使用 watchdog 实现类似 Linux 中 tail -f 的功能

一、代码实现 import logging import os import threading import timefrom watchdog.events import FileSystemEventHandler from watchdog.observers import Observerlogger logging.getLogger(__name__)class LogWatcher(FileSystemEventHandler):def __init__(self, log_…

《opencv实用探索·十五》inRange二值化图像

opencv接口如下&#xff1a; void inRange(InputArray src, InputArray lowerb, InputArray upperb, OutputArray dst);函数实现二值化功能&#xff0c;主要是将在两个阈值内的像素值设置为白色&#xff08;255&#xff09;&#xff0c;而不在阈值区间内的像素值设置为黑色&am…

一篇文章带你快速入门 Nuxt.js 服务端渲染

1. Nuxt.js 概述 1.1 我们一起做过的SPA SPA&#xff08;single page web application&#xff09;单页 Web 应用&#xff0c;Web 不再是一张张页面&#xff0c;而是一个整体的应用&#xff0c;一个由路由系统、数据系统、页面&#xff08;组件&#xff09;系统等等&#xff0…

HbuilderX使用Uniapp+Vue3安装uview-plus

如果你是vue2版本想使用uniapp去配置uviewui库可以参考之前的文章 小程序的第三方ui库推荐较多的还是uview的&#xff0c;看起来比较美观&#xff0c;功能也比较完善&#xff0c;下面将提一下Vue3安装uview-plus库的教程 创建项目 安装 首先进入官网 uView-Plus 直接下载并导…

预训练--微调

预训练–微调 一个很简单的道理&#xff0c;如果我们的模型是再ImageNet下训练的&#xff0c;那么这个模型一定是会比较复杂的&#xff0c;意思就是这个模型可以识别到很多种类别的即泛化能力很强&#xff0c;但是如果要它精确的识别是否某种类别&#xff0c;它的表现可能就不…

07-2 Python模块和命名空间

1. 模块 概念&#xff1a;其实就是一个Python文件&#xff0c;正常文件有的变量&#xff0c;函数&#xff0c;类&#xff0c;模块都有 功能:模块可以被其它程序引入&#xff0c;以使用该模块中的函数等功能。 示例&#xff1a;test-module.py调用mymodule.py模块中的now_time…

一篇文章带你快速入门 Vue 核心语法

一篇文章带你快速入门 Vue 核心语法 一、为什么要学习Vue 1.前端必备技能 2.岗位多&#xff0c;绝大互联网公司都在使用Vue 3.提高开发效率 4.高薪必备技能&#xff08;Vue2Vue3&#xff09; 二、什么是Vue 概念&#xff1a;Vue (读音 /vjuː/&#xff0c;类似于 view) …

Mysql 日期函数大全

一、时间函数 &#xff08;一&#xff09;、获取当前时间 1、NOW() 获取当前日期和时间&#xff0c;在程序一开始执行便拿到时间 返回格式 YYYY-MM-DD hh:mm:ss eg&#xff1a; NOW() 得到 2023-12-03 12:20:02 NOW(),SLEEP(2),NOW() 得到 2023-12-03 12:20:02 | 0 | 2023-…

目标检测——OverFeat算法解读

论文&#xff1a;OverFeat: Integrated Recognition, Localization and Detection using Convolutional Networks 作者&#xff1a;Pierre Sermanet, David Eigen, Xiang Zhang, Michael Mathieu, Rob Fergus, Yann LeCun 链接&#xff1a;https://arxiv.org/abs/1312.6229 文章…

SpringAOP专栏二《原理篇》

上一篇SpringAOP专栏一《使用教程篇》-CSDN博客介绍了SpringAop如何使用&#xff0c;这一篇文章就会介绍Spring AOP 的底层实现原理&#xff0c;并通过源代码解析来详细阐述其实现过程。 前言 Spring AOP 的实现原理是基于动态代理和字节码操作的。不了解动态代理和字节码操作…

【C语言】函数递归详解(一)

目录 1.什么是递归&#xff1a; 1.1递归的思想&#xff1a; 1.2递归的限制条件&#xff1a; 2.递归举例&#xff1a; 2.1举例1&#xff1a;求n的阶乘&#xff1a; 2.1.1 分析和代码实现&#xff1a; 2.1.2图示递归过程&#xff1a; 2.2举例2&#xff1a;顺序打印一个整数的…

机器学习---集成学习的初步理解

1. 集成学习 集成学习(ensemble learning)是现在非常火爆的机器学习方法。它本身不是一个单独的机器学 习算法&#xff0c;而是通过构建并结合多个机器学习器来完成学习任务。也就是我们常说的“博采众长”。集 成学习可以用于分类问题集成&#xff0c;回归问题集成&#xff…

多线程并发Ping脚本

1. 前言 最近需要ping地址&#xff0c;还是挺多的&#xff0c;就使用python搞一个ping脚本&#xff0c;记录一下&#xff0c;以免丢失了。 2. 脚本介绍 首先检查是否存在True.txt或False.txt文件&#xff0c;并在用户确认后进行删除&#xff0c;然后从IP.txt的文件中读取IP地…

CSS——sticky定位

1. 大白话解释sticky定位 粘性定位通俗来说&#xff0c;它就是相对定位relative和固定定位fixed的结合体&#xff0c;它的触发过程分为三个阶段 在最近可滚动容器没有触发滑动之前&#xff0c;sticky盒子的表现为相对定位relative【第一阶段】&#xff0c; 但当最近可滚动容…

【MATLAB】tvfEMD信号分解+FFT+HHT组合算法

有意向获取代码&#xff0c;请转文末观看代码获取方式~也可转原文链接获取~ 1 基本定义 TVFEMDFFTHHT组合算法是一种结合了总体变分模态分解&#xff08;TVFEMD&#xff09;、傅里叶变换&#xff08;FFT&#xff09;和希尔伯特-黄变换&#xff08;HHT&#xff09;的信号分解方…

电子学会C/C++编程等级考试2021年06月(五级)真题解析

C/C++等级考试(1~8级)全部真题・点这里 第1题:数字变换 给定一个包含5个数字(0-9)的字符串,例如 “02943”,请将“12345”变换到它。 你可以采取3种操作进行变换 1. 交换相邻的两个数字 2. 将一个数字加1。如果加1后大于9,则变为0 3. 将一个数字加倍。如果加倍后大于…

Python configparser 模块:优雅处理配置文件的得力工具

更多资料获取 &#x1f4da; 个人网站&#xff1a;ipengtao.com 配置文件在软件开发中扮演着重要的角色&#xff0c;而Python中的 configparser 模块提供了一种优雅而灵活的方式来处理各种配置需求。本文将深入介绍 configparser 模块的各个方面&#xff0c;通过丰富的示例代码…

嵌入式杂记 - MDK的Code, RO-data , RW-data, ZI-data意思

嵌入式杂记 - Keil的Code, RO-data , RW-data, ZI-data意思 MDK中的数据分类MCU中的内部存储分布MDK中数据类型存储Code代码段例子 RO-data 只读数据段例子 RW-data 可读写数据段例子 ZI-data 清零数据段例子 在嵌入式开发中&#xff0c;我们经常都会使用一些IDE&#xff0c;例…

Hadoop学习笔记(HDP)-Part.17 安装Spark2

目录 Part.01 关于HDP Part.02 核心组件原理 Part.03 资源规划 Part.04 基础环境配置 Part.05 Yum源配置 Part.06 安装OracleJDK Part.07 安装MySQL Part.08 部署Ambari集群 Part.09 安装OpenLDAP Part.10 创建集群 Part.11 安装Kerberos Part.12 安装HDFS Part.13 安装Ranger …

Web前端 ---- 【Vue】Vuex的使用(辅助函数、模块化开发)

目录 前言 Vuex是什么 Vuex的配置 安装vuex 配置vuex文件 Vuex核心对象 actions mutations getters state Vuex在vue中的使用 辅助函数 Vuex模块化开发 前言 本文介绍一种新的用于组件传值的插件 —— vuex Vuex是什么 Vuex 是一个专为 Vue.js 应用程序开发的状态…