• 单向环形链表介绍以及约瑟夫问题分析


    ❤️一名热爱Java的大一学生,希望与各位大佬共同学习进步❤️

    🧑个人主页:@周小末天天开心

    各位大佬的点赞👍 收藏⭐ 关注✅,是本人学习的最大动力

    感谢!

    📕该篇文章收录专栏—数据结构

    目录

    单向环形链表

    约瑟夫问题

    创建Boy类,用来存放数据

    创建一个单向环形链表类

    构建一个单向环形链表

    思路

    图解

    程序

    遍历环形链表

    思路

    图解

    程序

    根据用户输入,删除节点

    思路

    图解

    程序

    编写Joseph类进行演示

    查看输出结果


    单向环形链表

    从判断一个单链表是否存在循环而扩展衍生的问题,有则称之为有环链表问题,也就是经典的约瑟夫问题,也称为约瑟夫环。

    如下图所示:

    约瑟夫问题

    约瑟夫(约瑟夫环,Joseph)问题为:

    设编号为1,2,3,……,n 的n个人围坐在一圈,约定编号为k(1 <= k <= n)的人从1开始报数,数到m的那个人出列,他的下一位又从1开始报数,数到m的那个人又出列,以此类推,直到所有人出列为止,由此产生一个出队编号的序列。

    例如;

    n = 5,即有5个人

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

    m = 2,一次数2下

    提示:

            用一个不带头节点的循环链表来处理Joseph问题:首先构成一个有 n 个节点的单向环形链表,然后由 k 节点起从 1 开始计数,计数到 m 时,将对应的节点从链表中删除,然后再从被删除的节点的下一个节点又从 1 开始计数,直到最后一个节点从链表中删除,算法结束。

    创建Boy类,用来存放数据

    1. class Boy {
    2. private int no;//编号
    3. private Boy next;//指向下一个节点
    4. public Boy(int no) {
    5. this.no = no;
    6. }
    7. public int getNo() {
    8. return no;
    9. }
    10. public void setNo(int no) {
    11. this.no = no;
    12. }
    13. public Boy getNext() {
    14. return next;
    15. }
    16. public void setNext(Boy next) {
    17. this.next = next;
    18. }
    19. }

    创建一个单向环形链表类

    1. class CircleSingleLinkedList {
    2. }

    构建一个单向环形链表

    思路

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

    (2)后面每创建一个新的节点,就把该节点加入到已有的环形列表中即可

    图解

    程序

    1. //创建一个 first 节点,当前没有编号
    2. private Boy first = null;
    3. //添加节点,构成一个环形的链表
    4. public void addBoy(int nums) {
    5. //nums 表示节点的总个数
    6. //对 nums 做数据校验
    7. if(nums < 1) {
    8. System.out.println("nums的值不符合条件");
    9. return;
    10. }
    11. Boy curBoy = null;//辅助变量,帮助构造环形节点,因为first不能动
    12. //使用for循环来创建环形链表
    13. for (int i = 1; i <= nums; i++) {
    14. //根据编号,创建节点
    15. Boy boy = new Boy(i);
    16. //如果是第一个节点
    17. if(i == 1) {
    18. first = boy;//让 first 节点指向自己
    19. first.setNext(first);//构成环
    20. curBoy = first;//让 curBoy 指向第一个节点
    21. } else {
    22. curBoy.setNext(boy);
    23. boy.setNext(first);
    24. curBoy = boy;
    25. }
    26. }
    27. }

    遍历环形链表

    思路

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

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

    图解

    curBoy = first 时开始

     curBoy.next == first 时结束

    程序

    1. //遍历该链表
    2. public void showBoy() {
    3. //判断该链表是否为空
    4. if(first == null) {
    5. //说明该链表为空
    6. System.out.println("该链表为空");
    7. return;
    8. }
    9. //因为first指向第一个节点不能动,所以需要辅助指针完成遍历
    10. Boy curBoy = first;
    11. while(true) {
    12. System.out.println("节点的编号为" + curBoy.getNo());
    13. if(curBoy.getNext() == first) {
    14. //说明链表已经遍历完毕,
    15. //因为是环形链表,所以curBoy的下一个节点要与first作比较
    16. break;
    17. }
    18. curBoy = curBoy.getNext();//让curBoy后移一位
    19. }
    20. }

    根据用户输入,删除节点

    思路

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

    (2)当节点技术前,先让first 和 helper 移动k - 1次

    (3)当节点移动时,让 first 和 helper 指针同时移动m - 1次

    (4)这时就可以将 first 指向的节点出圈

    1)first = first.next;

    2)helper.next = first;

    (5)原来 first 指向的节点就没有任何引用,就会被回收

    图解

    程序

    1. /**
    2. *
    3. * @param startNo 表示从第几个节点开始计数
    4. * @param countNum 表示一次数几下
    5. * @param nums 表示最初有多少个节点在链表中
    6. */
    7. public void countBoy(int startNo , int countNum , int nums){
    8. //对数据进行校验,保证合理性
    9. if(first == null || startNo < 1 || startNo > nums) {
    10. System.out.println("参数输入有误,请从新输入");
    11. return;
    12. }
    13. //创建一个辅助指针,帮助节点出圈
    14. Boy helper = first;
    15. //辅助指针 helper 应该事先指向环形链表最后的节点
    16. while(true){
    17. if(helper.getNext() == first) {
    18. //说明helper已经指向了最后的一个节点
    19. break;
    20. }
    21. helper = helper.getNext();
    22. }
    23. //节点计数之前,先让 first 和 helper 移动 startNo - 1 次
    24. for (int i = 0; i < startNo - 1; i++) {
    25. first = first.getNext();
    26. helper = helper.getNext();
    27. }
    28. //当节点计数时,先让 first 和 helper 指针同时移动 countNum - 1 次,然后出圈
    29. //这是一个循环操作,知道圈中只有一个节点
    30. while(true) {
    31. if(helper == first) {//说明圈中只有一个节点
    32. break;
    33. }
    34. //让 first 和 helper 指针同时移动 countNum - 1 次
    35. for (int i = 0; i < countNum - 1; i++) {
    36. first = first.getNext();
    37. helper = helper.getNext();
    38. }
    39. //这时 first 指向的节点就是要出圈的节点
    40. System.out.printf("节点%d出圈\n",first.getNo());
    41. //这时可以将 first 指向的小孩节点出圈
    42. first = first.getNext();
    43. helper.setNext(first);
    44. }
    45. System.out.println("最后留在圈中的节点编号为:" + first.getNo());
    46. }

    编写Joseph类进行演示

    1. public class Joseph {
    2. public static void main(String[] args) {
    3. //构建环形链表
    4. CircleSingleLinkedList circleSingleLinkedList = new CircleSingleLinkedList();
    5. circleSingleLinkedList.addBoy(5);
    6. circleSingleLinkedList.showBoy();
    7. //测试节点出圈
    8. System.out.println("==============");
    9. circleSingleLinkedList.countBoy(1,2,5);
    10. }
    11. }

    查看输出结果

    当节点为 5 个时的输出结果:

    当节点为 10 个时的输出结果:

     

     

  • 相关阅读:
    神经网络(四)前馈神经网络
    首购2元起!CDN与加速特惠专场来啦~
    【MyBatis框架】核心配置文件讲解
    卡尔曼滤波器KF
    Hadoop总结
    puttygen工具ppk文件版本配置
    oh my zsh 失效 与 nvm 安装
    3.27每日一题(常系数线性非齐次方程的特解)
    pykafka的基本使用及统计kafka消息总数
    测试数据整理--chatgpt 构造sql语句导出数据库数据
  • 原文地址:https://blog.csdn.net/weixin_71646897/article/details/128046748