单向环形链表约瑟夫问题
提示:以下是本篇文章正文内容,下面案例可供参考
一、约瑟夫问题是什么?
Josephu 问题为:设编号为 1,2,… n 的 n 个人围坐一圈,约定编号为 k(1<=k<=n)的人从 1 开始报数,数
到 m 的那个人出列,它的下一位又从 1 开始报数,数到 m 的那个人又出列,依次类推,直到所有人出列为止,由
此产生一个出队编号的序列
思路:用一个不带头结点的循环链表来处理 Josephu 问题:先构成一个有 n 个结点的单循环链表,然后由 k 结
点起从 1 开始计数,计到 m 时,对应结点从链表中删除,然后再从被删除结点的下一个结点又从 1 开始计数,直
到最后一个结点从链表中删除算法结束
二、代码实现
1.节点类
public class Node {
int no;
Node next;
public Node(int no) {
this.no = no;
}
@Override
public String toString() {
return "Node{" +
"no=" + no +
'}';
}
}
2.代码实现
代码如下(示例):
public class GroupLinkedList {
Node first;
int size;
/**
* 动态创建环形链表
* @param size 链表的最大长度
*/
public GroupLinkedList(int size) {
if (size<1){
System.out.println("输入的值不正确");
return;
}
this.size=size;
Node cur=null;//用来指向单向链表的最后一个节点
for (int i = 1; i <size+1 ; i++) {
Node node=new Node(i);
if (i==1){
first=node;//第一个有效节点
first.next=first;//自己指向自己
cur=first;//记录最后的有效节点
}else {
cur.next=node;//在最后节点后面添加节点
node.next=first;//最后节点始终指向头结点形成环形
cur=cur.next;//后移
}
}
}
public Node getLast(){
if (first==null){
throw new RuntimeException("链表为空");
}
Node temp=first;
while (true){
if (temp.next==first){
return temp;
}
temp=temp.next;
}
}
/**
* 从第k个开始报数,每查m个就踢出一个
* @param k 从第k个开始查
* @param m 查m个
*/
public void quan(int k,int m){
//先判断k是否合理
if (k<0||k>size){
System.out.println("k值不合理");
return;
}
/*
因为是单向链表,如果想将数据剔除,
就要找到剔除数据的前一个位置 通过getlast方法找到单向链表的最后一个节点
*/
Node last=getLast();
Node head=first;//创建辅助节点
//如果从第k开始查,n那么现将last和head向后移动k-1个位置
for (int i = 0; i <k-1 ; i++) {
last=last.next;
head=head.next;
}
//没查m个数就踢出一个节点,同时将last和head移动m-1个位置,
//是的head指针指向被踢出节点,last节点指向head的前一个节点
while (true){
for (int i = 0; i <m-1 ; i++) {
last=last.next;
head=head.next;
}
System.out.println(head);//输出被删除节点
last.next=last.next.next;//将节点剔除
head=head.next;//让head指向下一个
if (last==last.next){//如果只剩下,直接输出,
// 这里判断方式很多,此时head和last的next都指向自身,并且last等于head
System.out.println(head);
return;
}
}
}
}
总结
思路就是,定义两个辅助指针a和b,a是从第几个开始查,b指向a身后的节点,在查数的时候两个指针同时查,让b指针删除a的指向,再让a指针的指向被删除后,a移动一位指向下一个有效节点,继续查,删除,直到最后只剩下一个节点的时候,直接删除就可以了