设编号为1,2,...n的n个人围成一个圈,约定编号为k(1<=k<=n)的人从1开始报数,数到m的那个人出列,他的下一位又从1开始报数,数到m的那个人又出列,依此类推,直到所有人出列位置,由此产生一个出队编号的序列
其实和之前的很多问题一样,一般就是考虑是否需要一个临时节点来辅助计算,如果需要,这个临时节点的位置应该在哪里?下面画了几张图来理解.
上图是准备开始,这个k因为是1(从第一个人开始报数),所以first指针指向1
因为m是2(报数从1到2),所以节点3需要出圈,这时候就要把节点2的next指向4,把节点3的next赋值给first(因为下一次报数要从节点4开始)
如上图,就开始了第二次报数.
从这个流程中不难看出,一个节点X的出圈,必定是要将X的前一个节点的next指向X的next.
那我们怎么获得这个X的前一个节点呢?(在first指向节点X的情况下)
只能是使用到辅助节点,来记录要被删除的节点的上一个节点,来完整修改指针指向的 *** 作.
接下来就是循环的编写问题了,谨慎一些即可
整体代码(注释很详细,应该很好理解)public class Josephu { public static void main(String[] args) { CircleSinglelinkedList circleSinglelinkedList = new CircleSinglelinkedList(); int num = 5; int k = 1; int m = 2; // 一共有num个人 // 约定编号为k(1<=k<=n)的人从1开始报数,数到m的那个人出列, // 他的下一位又从1开始报数,数到m的那个人又出列,依此类推 circleSinglelinkedList.josephu(num,k,m); } } //创建一个单向环形链表队列 class CircleSinglelinkedList{ // TODO: 2021/12/1 区分:first是头节点,在之前的单链表中的header是指向头结点 private Man first = null; //创建链表 public void create(int num){ if (num < 1){ System.out.println("编号不合法"); return; } //创建一个临时变量,这个变量用来记录遍历到的节点 Man tmp = null; //因为编号从1开始,所以i=1;i<=num for (int i = 1; i <= num; i++) { Man man = new Man(i); if (i == 1){ //头结点需要单独拎出来 first = man; first.setNext(man); tmp = first; }else{ //tmp就是当前的最后一个节点 //将tmp的next指向新节点 tmp.setNext(man); //新节点的next指向头节点 man.setNext(first); //将tmp变成最后一个节点,以供后续循环使用 tmp = man; } } } //遍历链表 public void show(){ if (first == null){ System.out.println("没有节点"); return; } Man tmp = first; do { System.out.println(tmp); tmp = tmp.getNext(); //当tmp等于first时,可以认定循环链表已经遍历完成,跳出循环 } while (tmp != first); } TODO: 2021/12/1 解决约瑟夫问题 public void josephu(int num, int k,int m) { int order = 1; create(num); //先找到开始的节点,但是要先进行一个合法性判断 if (k > num || num < 1 || m < 0){ System.out.println("参数非法"); return; } Man tmp = first; do { tmp = tmp.getNext(); }while (tmp.getNext() != first); //当tmp跳出循环时,就代表tmp已经是first后面的那个节点 //至于为什么要拿到这个first后面的节点,后面就会知道了 //报数前先将first指针指向开始节点,tmp指向开始的前一个节点 for (int i = 0; i < k - 1; i++) { first = first.getNext(); tmp = tmp.getNext(); } //现在两个指针调整到位 //开始报数 //循环中止的条件是tmp和first都指向同一节点,即链表中只剩一个节点 while (tmp != first) { for (int i = 0; i < m - 1; i++) { first = first.getNext(); tmp = tmp.getNext(); } //现在first指向的就是要出圈的人 System.out.println("第" + order++ + "个出圈的是: 编号" + first.getNo()); first = first.getNext(); //这个操作把first的下一节点赋值给了first tmp.setNext(first); //这个操作把出圈节点的前一个节点的next指向了first的位置 //以上两行就相当于时删除节点的操作,应该很好理解 } System.out.println("第" + order + "个出圈的是: 编号" + first.getNo()); } } //创建一个man类,表示链表中的节点 class Man { private int no; //编号 private Man next; //指向下一个节点 //一个构造器 public Man(int no){ this.no = no; } // public int getNo() { return no; } public void setNo(int no) { this.no = no; } public Man getNext() { return next; } public void setNext(Man next) { this.next = next; } @Override public String toString() { return "Man{" + "no=" + no + '}'; } }
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)