涉及 创建单向环形链表、添加节点、遍历节点、节点出圈 功能

    1. package com.atguigu.linkedlist;
    2. /**
    3. * 约瑟夫问题
    4. * 单向环形链表
    5. *
    6. * @author Dxkstart
    7. * @create 2021-10-03-17:10
    8. */
    9. public class Josepfu {
    10. public static void main(String[] args) {
    11. //测试
    12. CircleSingleLinkedList circleSingleLinkedList = new CircleSingleLinkedList();
    13. circleSingleLinkedList.addBoy(5);//加入5个小孩节点
    14. circleSingleLinkedList.showBoy();
    15. //测试小孩出圈
    16. circleSingleLinkedList.countBoy(1,2,5);
    17. }
    18. }
    19. //创建一个Boy类,表示一个节点
    20. class Boy {
    21. private int no;
    22. private Boy next;
    23. public Boy(int no) {
    24. this.no = no;
    25. }
    26. public int getNo() {
    27. return no;
    28. }
    29. public void setNo(int no) {
    30. this.no = no;
    31. }
    32. public Boy getNext() {
    33. return next;
    34. }
    35. public void setNext(Boy next) {
    36. this.next = next;
    37. }
    38. }
    39. //创建一个单向的环形链表
    40. class CircleSingleLinkedList {
    41. //创建一个first节点,当前没有编号
    42. private Boy first = null;
    43. public Boy getFist() {
    44. return first;
    45. }
    46. //添加小孩节点,构建成一个环形的链表
    47. public void addBoy(int nums) {
    48. //nums 做一个数据校验
    49. if (nums < 1) {
    50. System.out.println("nums的值不正确");
    51. return;
    52. }
    53. //中间变量指针
    54. Boy curBoy = null;
    55. //使用for来创建我们的环形链表
    56. for (int i = 1; i <= nums; i++) {
    57. //
    58. Boy boy = new Boy(i);
    59. if (i == 1) {
    60. first = boy;
    61. first.setNext(first);//构成环,自己指向自己
    62. curBoy = first;
    63. } else {
    64. curBoy.setNext(boy);//中间变量指针,指向新节点,打开环
    65. boy.setNext(first);//新的节点指向第一个节点,构成环
    66. curBoy = boy;//中间变量指针,指向新节点
    67. }
    68. }
    69. }
    70. //遍历当前环形链表
    71. public void showBoy() {
    72. //判断是否为空
    73. if (first == null) {
    74. System.out.println("链表为空");
    75. return;
    76. }
    77. //因为first不能动,因此我们仍然使用一个辅助指针完成遍历
    78. Boy curBoy = first;
    79. while (true) {
    80. System.out.printf("小孩的编号为:%d \n", curBoy.getNo());
    81. if (curBoy.getNext() == first) {//说明已经遍历完毕
    82. break;
    83. }
    84. curBoy = curBoy.getNext();//curBoy后移
    85. }
    86. }
    87. //根据用户的输入,计算出小孩的出圈顺序
    88. /**
    89. * @param startNo 表示从第几个小孩开始数数
    90. * @param countNum 表示每次数几下
    91. * @param nums 表示最初有几个小孩在圈中
    92. */
    93. public void countBoy(int startNo, int countNum, int nums) {
    94. //先对数据进行校验
    95. if (first == null || startNo < 1 || startNo > nums) {
    96. System.out.println("参数输入有误,请重新输入");
    97. return;
    98. }
    99. //创建辅助指针
    100. Boy helper = first;
    101. //事先将辅助指针helper指向最后一个节点
    102. while (true) {
    103. if (helper.getNext() == first) {
    104. break;
    105. }
    106. helper = helper.getNext();
    107. }
    108. //小孩报数前,先让 first 和 helper 移动k-1次(即first移动到第一个要报数的小孩那里)
    109. for (int i = 0; i < startNo-1; i++) {
    110. first = first.getNext();
    111. helper = helper.getNext();
    112. }
    113. //开始报数
    114. //当小孩报数时,让first 和 helper指针同时移动m-1次,然后出圈
    115. //这里是一个循环操作,直到权值只有一个节点
    116. while (true){
    117. if (helper == first){//此时圈中只有一个人
    118. System.out.println("结束");
    119. break;
    120. }
    121. //让first 和 helper 指针同时移动 countNum-1 次
    122. for (int i = 0; i < countNum-1; i++) {
    123. first = first.getNext();
    124. helper = helper.getNext();
    125. }
    126. //这时first指向的节点,就是延出圈的小孩节点
    127. System.out.printf("小孩%d出圈 \n" , first.getNo());
    128. //将first指向的节点出圈
    129. first = first.getNext();
    130. helper.setNext(first);
    131. }
    132. System.out.printf("最后留在圈中的小孩编号为%d \n",first.getNo());
    133. }
    134. }