约瑟夫问题 Josephu 问题为:设编号为 1,2,… n 的 n 个人围坐一圈,约定编号为 k(1<=k<=n)的人从 1 开始报数,数到 m 的那个人出列,它的下一位又从 1 开始报数,数到 m 的那个人又出列,依次类推,直到所有人出列为止,由此产生一个出队编号的序列。 提示:用一个不带头结点的循环链表来处理 Josephu 问题:先构成一个有 n 个结点的单循环链表,然后由 k 结点起从 1 开始计数,计到 m 时,对应结点从链表中删除,然后再从被删除结点的下一个结点又从 1 开始计数,直到最后一个结点从链表中删除算法结束。
单向环形链表解决约瑟夫问题
实现思路 (1)构建单向环形链表 (2)小孩出圈
代码实现
package com.datastrucate.linkedlist;/**
* ClassName:Josepfu
* Package:com.datastrucate.linkedlist
* Description:
*
* @Date:2021/5/11 15:00
* @Author:hm
*/publicclassJosepfu{publicstaticvoidmain(String[] args){// 测试一把看看构建环形链表,和遍历是否 ok
CircleSingleLinkedList circleSingleLinkedList =newCircleSingleLinkedList();
circleSingleLinkedList.addBoy(125);// 加入 5 个小孩节点
circleSingleLinkedList.showBoy();//测试一把小孩出圈是否正确
circleSingleLinkedList.countBoy(10,20,125);// 2->4->1->5->3}}// 创建一个环形的单向链表classCircleSingleLinkedList{// 创建一个 first 节点,当前没有编号private Boy first = null;// 添加nums个小孩节点,构建成一个环形的链表publicvoidaddBoy(int nums){// nums 做一个数据校验if(nums <1){
System.out.println("nums 的值不正确");return;}
Boy curBoy = null;// 辅助指针,帮助构建环形链表// 使用 for 来创建我们的环形链表for(int i =1; i <= nums; i++){// 根据编号,创建小孩节点
Boy boy =newBoy(i);// 如果是第一个小孩,构建一个闭环if(i ==1){
first = boy;
first.setNext(first);// 构成环
curBoy = first;// 让 curBoy 指向第一个小孩}else{//添加节点
curBoy.setNext(boy);//
boy.setNext(first);//
curBoy = boy;}}}// 遍历当前的环形链表publicvoidshowBoy(){// 判断链表是否为空if(first == null){
System.out.println("没有任何小孩~~");return;}// 因为 first 不能动,因此我们仍然使用一个辅助指针完成遍历
Boy curBoy = first;while(true){
System.out.printf("小孩的编号 %d \n", curBoy.getNo());if(curBoy.getNext()== first){// 说明已经遍历完毕break;}
curBoy = curBoy.getNext();// curBoy 后移}}/**
*根据用户的输入,计算出小孩出圈的顺序
* @param startNo
* 表示从第几个小孩开始数数
* @param countNum
* 表示数几下
* @param nums
* 表示最初有多少小孩在圈中
*/publicvoidcountBoy(int startNo,int countNum,int nums){// 先对数据进行校验if(first == null || startNo <1|| startNo > nums){
System.out.println("参数输入有误, 请重新输入");return;}// 创建要给辅助指针,帮助完成小孩出圈
Boy helper = first;//① 初始化指针位置 让helper事先应该指向环形链表的最后这个节点while(true){if(helper.getNext()== first){// 说明 helper 指向最后小孩节点break;}
helper = helper.getNext();}//② 小孩报数前,先让 first 和 helper 移动 startNo - 1 次来到开始数数的位置for(int j =0; j < startNo -1; j++){
first = first.getNext();
helper = helper.getNext();}//③ 当小孩报数时,让 first 和 helper 指针同时移动 countNum - 1 次, 然后出圈//这里是一个循环操作,直到圈中只有一个节点while(true){if(helper == first){//说明圈中只有一个节点break;}//让 first 和 helper 指针同时 的移动 countNum - 1for(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());}}// 创建一个 Boy 类,表示一个节点classBoy{privateint no;// 编号private Boy next;// 指向下一个节点,默认 nullpublicBoy(int no){this.no = no;}publicintgetNo(){return no;}publicvoidsetNo(int no){this.no = no;}public Boy getNext(){return next;}publicvoidsetNext(Boy next){this.next = next;}}
数据结构与算法之环形链表实现约瑟夫问题约瑟夫问题Josephu 问题为:设编号为 1,2,… n 的 n 个人围坐一圈,约定编号为 k(1<=k<=n)的人从 1 开始报数,数到 m 的那个人出列,它的下一位又从 1 开始报数,数到 m 的那个人又出列,依次类推,直到所有人出列为止,由此产生一个出队编号的序列。提示:用一个不带头结点的循环链表来处理 Josephu 问题:先构成一个有 n 个结点的单循环链表,然后由 k 结点起从 1 开始计数,计到 m 时,对应结点从链表中删除,然后再