环形链表解释约瑟夫问题

news2024/11/24 0:11:10

环形链表解释约瑟夫问题

来自尚硅谷开放课程的迁移学习,致敬尚硅谷的各位老师,受益匪浅!!!

单向链表,双向链表,环形链表对比介绍

单向链表、双向链表和环形链表都是常见的链表数据结构,它们在不同场景下具有不同的特点和优缺点。

单向链表:

特点:

  • 插入和删除操作只能在头部进行;

  • 遍历整个链表时,时间复杂度为O(n)。

使用场景:

  • 当需要在链表头插入或删除元素时,可以选择单向链表;

  • 需要高效地遍历整个链表时,可以使用单向链表。

优点:

  • 插入和删除操作简单;

  • 遍历整个链表的时间复杂度较低。

缺点:

  • 只能在头部进行插入和删除操作;

  • 不适用于需要在任意位置插入或删除元素的场景。

双向链表:

特点:

  • 在任意位置都可以进行插入和删除操作;

  • 遍历整个链表的时间复杂度为O(1)。

使用场景:

  • 需要频繁地在链表中插入和删除元素时,可以选择双向链表;

  • 需要高效地遍历整个链表时,可以使用双向链表。

优点:

  • 可以方便地在任意位置插入和删除元素;

  • 遍历整个链表的时间复杂度较低。

缺点:

  • 由于需要维护两个指针,空间开销较大;

  • 在插入和删除操作时,可能需要移动后面的节点。

环形链表:

特点:

  • 每个节点都指向前一个节点的下一个节点;

  • 如果某个节点不存在,则形成一个环。

使用场景:

  • 当需要表示循环的数据结构时,可以选择环形链表;

  • 需要快速查找某个节点的前驱节点时,可以使用环形链表。

优点:

  • 可以方便地表示循环的数据结构;

  • 在查找前驱节点时,时间复杂度为O(1)。

缺点:

  • 每个节点都需要指向前一个节点的下一个节点,空间开销较大;

约瑟夫问题引入

约瑟夫问题是一个经典的数学问题,它描述了有n个人围成一圈,从第一个人开始报数,报到m的人出圈,直到只剩下一个人。问这个人是谁。这个问题可以用循环链表来解决。

具体来说,我们可以定义一个长度为n的循环链表,其中每个节点包含两个信息:当前的位置i和已经出圈的人数count。初始时,第一个节点的位置为1,count为0;其余节点的位置为2、4、6…n-1,count均为1。然后从第一个节点开始报数,每报到一个位置i的人出圈,同时更新该节点的count值。当count变为n-1时,指向该节点的前一个节点即为最后幸存者。

这个方法的时间复杂度是O(n),空间复杂度是O(1)。因此,它是一种高效的解决方案。

环形链表与约瑟夫问题之间的关联在于,我们可以使用环形链表来模拟约瑟夫问题的过程。具体来说,我们可以将每个人看作一个节点,将他们的位置作为该节点的值,将已经出圈的人数作为该节点的count值。这样,我们就可以使用环形链表来模拟约瑟夫问题的过程了。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-6EOvdAzi-1684058523540)(C:\Users\ASUS\AppData\Roaming\Typora\typora-user-images\image-20230514161609028.png)]

思路解析

约瑟夫问题-创建环形链表的思路:

构建一个单向的环形链表

1.先创建第一个节点,让first指向该节点,并且形成环形

2.后面我们每创建一个新的节点,就把该节点加入已有的环形链表中

遍历环形链表

1.先让第一个辅助指针(变量)curBoy,指向first节点

2.然后通过一个while循环遍历该环形链表即可curBoy.next==first结束

约瑟夫问题-小孩出圈的思路分析:

根据用户的输入,生成有n个小孩出圈的顺序

n=5,即有5个人

k=1,从第一个人开始报数

m=2,数两下

1.需要创建一个辅助指针(变量)helper,事先应该指向环形链表的最后这一个节点

2.小孩报数前,先让first和helper移动k-1次

3.当小孩报数时,让first和helper指针同时移动m-1次

4.这时就可以将first指向的小孩节点出圈

first=first.next

helper.next=first

原来first指向的节点就没有任何作用,就会被回收

出圈的顺序

2->4->1->5->3

代码描述

package com.success.day05;

/**
 * @Description 创建一个Boy实体类
 * @Author IT小辉同学
 * @Date 2023/05/14
 */
class Boy {
    private int no; //编号
    private Boy next; //指向下一个节点,默认为null

    /**
     * @Description 构造函数
     * @Author IT小辉同学
     * @Date 2023/05/14
     */
    public Boy(int no) {
        this.no = no;
    }

    public int getNo() {
        return no;
    }

    public void setNo(int no) {
        this.no = no;
    }

    public Boy getNext() {
        return next;
    }

    public void setNext(Boy next) {
        this.next = next;
    }
}

/**
 * @Description 创建一个环形单向链表
 * @Author IT小辉同学
 * @Date 2023/05/14
 */
class CircleSingleLinkedList {
    //创建一个first节点,当前没有编号
    private Boy first = null;

    /**
     * @Description 添加小孩节点,形成一个环形的链表
     * @Author IT小辉同学
     * @Date 2023/05/14
     */
    public void addBoy(int nums) {
        //对于nums数据进行校验
        if (nums < 1) {
            System.out.printf("nums的值不正确");
            return;
        }
        //创建辅助指针,帮助构建环形链表
        Boy curBoy = null;
        //循环创建环形链表
        for (int i = 1; i <= nums; i++) {
            //根据编号,创建小孩节点
            Boy boy = new Boy(i);
            //如果是第一个小孩
            if (i == 1) {
                first = boy;
                first.setNext(first); //构成环
                curBoy = first;
            } else {
                curBoy.setNext(boy);
                boy.setNext(first);
                curBoy = boy;
            }
        }
    }

    /**
     * @Description 遍历当前的环形链表
     * @Author IT小辉同学
     * @Date 2023/05/14
     */
    public void showBoy() {
        //判断链表是否为空
        if (first == null) {
            System.out.printf("没有任何小孩~~");
            return;
        }
        // 因为 first 不能动,因此我们仍然使用一个辅助指针完成遍历
        Boy curBoy = first;
        while (true) {
            System.out.printf("小孩的编号 %d \n", curBoy.getNo());
            //判断是不是遍历到了最后一个节点
            if (curBoy.getNext() == first) {
                break;
            }
            // curBoy 后移
            curBoy = curBoy.getNext();
        }
    }

    /**
     * @param countNum 表示数几下
     * @param nums     表示最初有多少小孩在圈中
     * @Param startNo 表示数几下
     * @Return
     * @Description 根据用户的输入,计算出小孩出圈的顺序
     * @Author IT小辉同学
     * @Date 2023/05/14
     */
    public void countBoy(int startNo, int countNum, int nums) {
        //先对数据进行校验
        if (first == null || startNo < 1 || startNo > nums) {
            System.out.println("参数有误,请重新输入");
            return;
        }
        //创建辅助指针,帮助完成小孩出圈
        Boy helper = first;
        //需求创建一个辅助指针(变量)helper,事先应该指向环形链表的最后这个节点
        while (true) {
            //说说明helper已经指向了最后一个小孩节点
            if (helper.getNext() == first) {
                break;
            }
            helper = helper.getNext();
        }
        //小孩报数前,先让 first 和 helper 移动 k - 1 次
        for (int j = 0; j < startNo - 1; j++) {
            first = first.getNext();
            helper = helper.getNext();
        }
        //当小孩报数时,让 first 和 helper 指针同时 的移动 m - 1 次, 然后出圈
        //这里是一个循环操作,知道圈中只有一个节点
        while (true) {
            if (helper == first) { //说明圈中只有一个节点
                break;
            }
            //让 first 和 helper 指针同时 的移动 countNum - 1
            for (int j = 0; j < countNum - 1; j++) {
                first = first.getNext();
                helper = helper.getNext();
            }
            //这时 first 指向的节点,就是要出圈的小孩节点
            System.out.printf("小孩%d 出圈\n", first.getNo());
            //这时将 first 指向的小孩节点出圈
            first = first.getNext();
            helper.setNext(first);
        }
        System.out.printf("最后留在圈中的小孩编号%d \n", first.getNo());
    }
}

public class Josepfu {
    public static void main(String[] args) {
        // 测试一把看看构建环形链表,和遍历是否 ok
        CircleSingleLinkedList circleSingleLinkedList = new CircleSingleLinkedList();
        circleSingleLinkedList.addBoy(5);// 加入 5 个小孩节点
        circleSingleLinkedList.showBoy();
        //测试一把小孩出圈是否正确
        circleSingleLinkedList.countBoy(1, 6, 5); // 2->4->1->5->3
    }
}

结果

小孩的编号 1
小孩的编号 2
小孩的编号 3
小孩的编号 4
小孩的编号 5
小孩1 出圈
小孩3 出圈
小孩2 出圈
小孩5 出圈
最后留在圈中的小孩编号4

进程已结束,退出代码0

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

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

相关文章

介绍如何在 MySQL 中创建新用户并授予权限?

MySQL 是一个开源的关系型数据库管理系统&#xff0c;常用于存储和管理大量的结构化数据。在使用 MySQL 进行数据管理时&#xff0c;为了安全和方便管理&#xff0c;通常需要创建新用户并授予相应的权限。本文将介绍如何在 MySQL 中创建新用户并授予权限的方法。 创建新用户 在…

第十一章结构性模式—组合模式

文章目录 组合模式解决的问题概念结构 实例组合模式的分类优点使用场景 结构型模式描述如何将类或对象按某种布局组成更大的结构&#xff0c;有以下两种&#xff1a; 类结构型模式&#xff1a;采用继承机制来组织接口和类。 对象结构型模式&#xff1a;釆用组合或聚合来组合对…

shell脚本之“awk“命令

文章目录 1.awk工作原理2.awk命令演示操作部分2.1 按行输出文本演示操作2.2 BEGIN模式演示操作2.3 按字段输出文本演示操作2.4 通过管道、双引号调用Shell命令2.5 date命令演示操作2.6 getline参数详解2.7 awk命令的数组操作 3. 总结 1.awk工作原理 逐行读取文本&#xff0c;默…

位域和字节对齐

结构体中的位域 位域是一种特殊的结构体成员&#xff0c;它允许将一个字节或多个字节中的每个位作为一个独立的成员来使用。位域的语法形式为&#xff1a; struct {type [member_name] : width; }; 其中&#xff0c;type 表示位域成员的类型&#xff0c;可以是 int、unsigne…

网络编程——TCP编程

TCP编程 流程服务器客户端 函数接口1、socket2、bind3、listen4、accept5、recv6、send7、connet 实现双工通信server.ccelient.c优化代码 流程 在C语言中进行TCP编程的一般步骤如下&#xff1a; &#xff08;1&#xff09;包含头文件&#xff1a; 在代码中包含必要的头文件&a…

面对象QgsPolygon

几何对象中的面用QgsPolygon进行封装&#xff0c;也称为多边形简单的多边形是由一串点连接而成&#xff0c;并首尾闭合多边形的结构更复杂&#xff0c;除了有一个外部轮廓&#xff0c;还可能包括内部多个轮廓 创建面对象 QgsPolygon() #创建一个空的面 使用setExteriorRing设…

Spring AOP 实践指南

Spring AOP 实践指南 文章目录 Spring AOP 实践指南一、概述1、简介2、官方资料3、本文档说明 二、基本使用1、引入依赖2、定义切面3、定义切点4、创建 HelloController5、启动项目&#xff0c;访问测试 三、通知1、概述五种通知通知的顺序 2、通知方法接受的参数3、前置通知代…

Thread线程学习(1) 了解线程的基本知识——什么是线程

本专栏将记录有关线程方面的知识 在计算机科学领域中&#xff0c;线程&#xff08;Thread&#xff09;是一种执行计算机程序的基本单元。对于初学者来说&#xff0c;理解线程是学习并发编程的关键一步。本文将带你了解线程的基础知识&#xff0c;包括线程的定义、线程与进程的关…

GPT神奇应用:给孩子做每日安排

正文共 1163 字&#xff0c;阅读大约需要 4 分钟 家长必备技巧&#xff0c;您将在4分钟后获得以下超能力&#xff1a; 快速生成每日安排计划 Beezy评级 &#xff1a;B级 *经过简单的寻找&#xff0c; 大部分人能立刻掌握。主要节省时间。 推荐人 | Kim 编辑者 | Linda ●图片…

JavaScript实现打印倒金字塔的代码

以下为实现打印倒金字塔的程序代码和运行截图 目录 前言 一、实现打印倒金字塔 1.1 运行流程及思想 1.2 代码段 1.3 JavaScript语句代码 1.4 运行截图 前言 1.若有选择&#xff0c;您可以在目录里进行快速查找&#xff1b; 2.本博文代码可以根据题目要求实现相关使用功…

动态规划专题

动态规划专题 最长递增子序列LeetCode 300. 最长递增子序列解题思路代码实现 LeetCode 354. 俄罗斯套娃信封问题解题思路代码实现 总结 不要纠结&#xff0c;干就完事了&#xff0c;熟练度很重要&#xff01;&#xff01;&#xff01;多练习&#xff0c;多总结&#xff01;&…

【Redis】Redis中bitmap的原理和使用

文章目录 一、原理二、BitMap 相关命令三、BitMap 空间计算四、使用场景1. 用户签到2. 统计活跃用户&#xff08;用户登陆情况&#xff09;3. 统计用户在线状态4. 实现布隆过滤器 五、总结 一、原理 先声明一下&#xff1a;Redis 有5种数据类型&#xff0c;而 BitMap 在 Redis…

【k8s】Ubuntu22.04离线部署k8s集群:搭建软件仓库和镜像仓库(repo节点)

上两篇主要记录了在CentOS 7环境中离线部署k8s的方案&#xff0c;本篇继续介绍方案二在Ubuntu 22.04的实现。&#xff08;当然&#xff0c;整体思路还是跟上篇基本相似&#xff09; 目录 Ubuntu22.04离线部署k8s集群&#xff1a;搭建软件仓库和镜像仓库&#xff08;repo节点&am…

总结852

学习目标&#xff1a; 月目标&#xff1a;5月&#xff08;张宇强化前10讲&#xff0c;背诵15篇短文&#xff0c;熟词僻义300词基础词&#xff09; 周目标&#xff1a;张宇强化前5讲并完成相应的习题并记录&#xff0c;英语背3篇文章并回诵 每日必复习&#xff08;5分钟&#…

云上高校导航 小程序 开发教程 1.0.1

​ Gitee仓库&#xff1a;云上高校导航 GitHub仓库&#xff1a;云上高校导航 “云上高校导航”是一套基于小程序云开发的校园导航类系统开发方案&#xff0c;该开发方案可供开发者进行二次开发&#xff0c;用于解决师生和访客的校园出行需求。 项目优势及创新&#xff1a;…

ChatGPT vs. Bing vs. Bard

随着 2022 年 ChatGTP 的推出&#xff0c;人工智能聊天机器人的世界突然走上了一条新道路。如今&#xff0c;密切关注 AI 的人都知道&#xff0c;不同公司推出了几款产品。从谷歌拥有自己的 Bard AI&#xff0c;到微软发布新的 Bing AI Chat&#xff0c;再到 OpenAI 发布GPT-4。…

云服务器搭建Python项目实现学术优化chatgpt

云服务器搭建实现学术优化chatgpt 1 服务器准备2 云服务器配置2.1 python虚拟环境2.1.1 python3.9安装配置2.1.2 下载python项目2.1.3 创建python虚拟环境 3 后台运行python项目&#xff08;不然不能关闭与云服务器的连接&#xff0c;那意义何在&#xff1f;&#xff09; 1 服务…

GEE:将年度NDVI时间序列影像集合(Image Collection)转变为多波段影像,并下载

作者:CSDN @ _养乐多_ 本文将重点介绍如何使用 Google Earth Engine (GEE) 将多波段影像堆叠并导出,并探讨其应用场景和好处。 通过使用 GEE 的多波段影像堆叠功能,我们可以将不同波段的遥感影像整合成一个多波段影像,以支持各种地理空间分析任务。这种方法适用于遥感影…

以太网端口类型

以太网端口有 3种链路类型:access、trunk、hybird Access类型端口只能属于1个VLAN 般用于连接计算机 端口&#xff1b;Trunk类型端口可以允许多个VLAN通过,可以接收和发送多个VLAN 报文,一般用于交换机之间的连接&#xff1b;Hybrid类型端口可以允许多个VLAN通过&#xff0c;可…

Dubbo的使用

Dubbo在开发中&#xff0c;存在两种开发思路。基于SOA(面向服务的体系架构)思想和辅助SpringCloud架构提升效率。 Dubbo 默认使用 Netty 框架 Dubbo基于SOA思想 正常SpringBoot项目是只有一个启动类&#xff0c;接口定义在web层(即Controller层)中,然后调用Service层。&…