问题描述
1.Josephu 问题为:设编号为 1,2,… n 的 n 个人围坐一圈,约定编号为 k(1<=k<=n)的人从 1 开始报数,数到 m 的那个人出列,它的下一位又从 1 开始报数,数到 m 的那个人又出列,依次类推,直到所有人出列为止,由此产生一个出队编号的序列。
2.提示:用一个不带头结点的循环链表来处理 Josephu 问题:先构成一个有 n 个结点的单循环链表,然后由 k 结点起从 1 开始计数,计到 m 时,对应结点从链表中删除,然后再从被删除结点的下一个结点又从 1 开始计数,直到最后一个结点从链表中删除算法结束。
单向循环链表介绍
构造链表
1.初始化
对于first节点本身构成一个环
first.next=first;
2.本文采用两种方式进行插入
(1)单个节点采用尾插法(即找到最后一个节点,在最后一个节点的后面进行插入)
Node temp=null;
temp=first;
//判断是否只有一个节点
if(temp.next==first){
first.id=newNode.id;
first.num=newNode.num;
}
else{
while(temp.next!=first){
temp=temp.next;
}
//找到最后一个节点
newNode.next=temp.next;
temp.next=newNode;
}
(2)按顺序插入一堆节点(每次都往cur后面插入)
Node cur=first;
//初始化first
first.id=list.get(0).id;
first.num=list.get(0).num;
for(int i=2;i<=list.size();i++){
Node t=list.get(i-1);
t.next=cur.next;
cur.next=t;
cur=t;
}
解决约瑟夫问题
1.根据用户的输入,生成一个出圈的顺序
2.创建一个创建辅助指针helper,指向first的前一个节点。
3.出圈前,先让first和helperfirst和help移动star-1次(让first移动到开始计数位置)。
4.循环过程
(1)开始计数后,每次让先让first和help移动count-1次。
(2)此时first指向的节点即为要出圈的节点
出圈:
first=first.next;
helper.next=first;
(3)当first==helper(指向同一个节点),此时只剩一个节点,即最后要出圈的节点。
while (true){
if(helper==first){//环中只有一个节点
break;
}
//
for(int i=0;i<count-1;i++){
first=first.next;
helper=helper.next;
}
//此时first指向的节点即为出圈的节点
System.out.println(first.toString());
first=first.next;
helper.next=first;
}
System.out.println(first.toString());
测试
假设:共5个节点,start=1;count=2;
那么,出圈的顺序:2-4-1-5-3
代码
public class Node {
public int id;
public int num;
public Node next;
public Node(int id,int num) {
this.id=id;
this.num = num;
}
@Override
public String toString() {
return "Node{" +
"id=" + id +
", num=" + num +
'}';
}
}
public class CircleSingleLinkedList {
//创建first
private Node first=new Node(1,1);
public CircleSingleLinkedList(){
//初始化
first.next=first;
}
//添加一个节点
public void addOneNode(Node newNode){
Node temp=null;
temp=first;
//判断是否只有一个节点
if(temp.next==first){
first.id=newNode.id;
first.num=newNode.num;
}
else{
while(temp.next!=first){
temp=temp.next;
}
//找到最后一个节点
newNode.next=temp.next;
temp.next=newNode;
}
}
//初始化一个环形链表(向后插入新节点)
public void addNodeList( List<Node> list){
if(list.size()<2){
throw new RuntimeException("个数必须大于1!");
}
Node cur=first;
//初始化first
first.id=list.get(0).id;
first.num=list.get(0).num;
for(int i=2;i<=list.size();i++){
Node t=list.get(i-1);
t.next=cur.next;
cur.next=t;
cur=t;
}
}
//遍历
public void printList(){
if(first.next==first){
throw new RuntimeException("个数必须大于一");
}
Node temp=null;
temp=first;
while(temp.next!=first){
System.out.println(temp.toString());
temp=temp.next;
}
}
//长度
public int getLenght(){
int lenght=1;
Node temp=null;
temp=first;
while(temp.next!=first){
lenght++;
temp=temp.next;
}
return lenght;
}
//解决约瑟夫问题
public void josephu(int star,int count){
//节点个数必须大于1,开始的位置不能小于1或大于环的长度
if(first.next==first||star<1||star>getLenght()){
throw new RuntimeException("参数有误");
}
//创建辅助指针,指向first的前一个节点
Node helper=first;
while (helper.next!=first){
helper=helper.next;
}
//first和help移动star-1次(让first移动到开始位置)
for(int i=0;i<star-1;i++){
first=first.next;
helper=helper.next;
}
//
while (true){
if(helper==first){//环中只有一个节点
break;
}
//
for(int i=0;i<count-1;i++){
first=first.next;
helper=helper.next;
}
//此时first指向的节点即为出圈的节点
System.out.println(first.toString());
first=first.next;
helper.next=first;
}
System.out.println(first.toString());
}
}
public class CircleSingleLinkedListTest {
@Test
public void addNodeList() {
CircleSingleLinkedList t=new CircleSingleLinkedList();
List<Node> list =new ArrayList<>();
list.add(new Node(1,1));
list.add(new Node(2,2));
list.add(new Node(3,3));
list.add(new Node(4,4));
list.add(new Node(5,5));
t.addNodeList(list);
t.printList();
System.out.println("-------------------------");
t.josephu(1, 2);
}
}
结果:
Node{id=1, num=1}
Node{id=2, num=2}
Node{id=3, num=3}
Node{id=4, num=4}
-------------------------
Node{id=2, num=2}
Node{id=4, num=4}
Node{id=1, num=1}
Node{id=5, num=5}
Node{id=3, num=3}