约瑟夫问题算法的实现(代码实现)
代码如下(我们将这个方法也定义到单向环形链表类中):
/**
* 实现约瑟夫问题解决算法(也就是按照指定规则出链表)
* @param startNo 开始报数的小孩
* @param countNum 报道多少后出链表
* @param nums 一共有多少个结点(单向环形链表的长度)
*/
public void countBoy(int startNo, int countNum, int nums){
//开始的时候先让helper指向first,然后使用helper进行遍历,直到遍历到最后一个元素位置
Boy helper = first;
//让我们的Boy指针链表中最后一个结点的位置
while(helper.getNext() != first){
helper = helper.getNext();
}
//当退出while循环的时候helper指向的就是first的前一个节点
//小孩开始报数之前先让first和helper移动startNo - 1次,一定startNo - 1次之后first就指向来开始报数的位置(也就是第startNo个结点)
for (int i = 0; i < startNo; i++) {
helper = helper.getNext();
first = first.getNext();
}
/*
然后让我们的first结点移动m - 1次,一直重复执行,每次移动m - 1次之后first就指向了待删除结点,那么我们删除了该待删除结点之后
first自然就会后移,就会指向我们删除位置的下一个节点,也就是开始报数的小孩,那么我们只需要再遍历m - 1次就可以又找到下一次的待删除位置了
然后一直重复执行,直到执行到我们的单向环形链表中只有一个节点为止(当环形链表中只有一个节点的时候我们就要退出了,因为当链表中只有一个节点的时候
这个时候first和helper指向了同一个节点,这个时候都指向了自己,通过上面的通用操作就删除不了这个最后一个节点了)
*/
while(true) {
if (first == helper){ //说明链表中只有一个节点了,那么此时就退出循环
break;
}
for (int i = 0; i < countNum - 1; i++) {
helper = helper.getNext();
first = first.getNext();
}
System.out.printf("小孩%d出圈",first.getNo());
System.out.println();
//执行删除结点的操作
first = first.getNext();
helper.setNext(first);
}
System.out.printf("最后的小孩%d出圈",first.getNo());
}
我们是将约瑟夫算法封装到了单向环形链表类中(这里我们给出我们的单向环形链表类):
package com.ffyc.linkedlist;
/**
* 单向环形链表类
*/
public class CircleSingleLinkedList {
//创建一个first指针
Boy first = null;
//创建一个curBoy指针
Boy curBoy = null;
//创建一个长度为nums的单向环形链表
public void addBoy(int nums){
//做一个数据校验(也就是做一个合法性判断)
if(nums < 1){
System.out.println("nums的值不正确");
return;
}
for (int i = 0; i < nums; i++) {
//每次循环都创建一个新的结点,这个创建的结点就是准备插入到单向环形链表中的结点
Boy boy = new Boy(i+1);
//判断,如果是第一个节点,这个时候我们要先让其形成一个环,并且还要完成first指针和curBoy指针的指向,所以对于第一个节点我们要进行一个特殊处理
//而特殊处理我们一般都是使用if语句来实现
if(i == 0){
first = boy; //让first指针指向第一个节点
curBoy = boy; //让curBoy指向唯一存在的结点(我们的curBoy指针是要指向环形链表中的最后一个结点的,但是这个时候显然我们的链表中只有一个节点)
first.setNext(first); //让first结点自己的next域指向自己,也就是自己和自己形成一个环
}
//对于不是第一个节点的情况
curBoy.setNext(boy); //然curBoy指针指向的结点的next域指向boy(也就是准备添加的结点)
curBoy = boy; //完成curBoy的后移(因为curBoy的后面就是boy结点,所有我们直接使用curBoy指针执行boy结点就是完成了结点的后移操作)
boy.setNext(first); //让boy结点的next域指向我们的单向环形链表中的第一个节点(也就是first指针指向的结点)
}
}
//为了测试我们写的代码是否正确,我们写一个遍历此单向环形链表的方法来测试
public void list(){
//我们要遍历链表,那么肯定是要通过一个临时变量来遍历
Boy temp = first;
/*
首先判断链表是否为空,如果链表为空则直接退出循环
*/
if(temp == null){
System.out.println("链表为空");
}
do{
System.out.println(temp.getNo()+",");
temp = temp.getNext();
}while(temp != first);
}
/**
* 实现约瑟夫问题解决算法(也就是按照指定规则出链表)
* @param startNo 开始报数的小孩
* @param countNum 报道多少后出链表
* @param nums 一共有多少个结点(单向环形链表的长度)
*/
public void countBoy(int startNo, int countNum, int nums){
//开始的时候先让helper指向first,然后使用helper进行遍历,直到遍历到最后一个元素位置
Boy helper = first;
//让我们的Boy指针链表中最后一个结点的位置
while(helper.getNext() != first){
helper = helper.getNext();
}
//当退出while循环的时候helper指向的就是first的前一个节点
//小孩开始报数之前先让first和helper移动startNo - 1次,一定startNo - 1次之后first就指向来开始报数的位置(也就是第startNo个结点)
for (int i = 0; i < startNo; i++) {
helper = helper.getNext();
first = first.getNext();
}
/*
然后让我们的first结点移动m - 1次,一直重复执行,每次移动m - 1次之后first就指向了待删除结点,那么我们删除了该待删除结点之后
first自然就会后移,就会指向我们删除位置的下一个节点,也就是开始报数的小孩,那么我们只需要再遍历m - 1次就可以又找到下一次的待删除位置了
然后一直重复执行,直到执行到我们的单向环形链表中只有一个节点为止(当环形链表中只有一个节点的时候我们就要退出了,因为当链表中只有一个节点的时候
这个时候first和helper指向了同一个节点,这个时候都指向了自己,通过上面的通用操作就删除不了这个最后一个节点了)
*/
while(true) {
if (first == helper){ //说明链表中只有一个节点了,那么此时就退出循环
break;
}
for (int i = 0; i < countNum - 1; i++) {
helper = helper.getNext();
first = first.getNext();
}
System.out.printf("小孩%d出圈",first.getNo());
System.out.println();
//执行删除结点的操作
first = first.getNext();
helper.setNext(first);
}
System.out.printf("最后的小孩%d出圈",first.getNo());
}
}
给出我们的结点类:
/**
* 单线环形链表的结点类
*/
class Boy{
private int no;
private Boy next;
public Boy(){
}
public Boy(int no) {
this.no = no;
}
public int getNo() {
return no;
}
public void setNo(int no) {
this.no = no;
}
public Boy getNext() {
return next;
}
public void setNext(Boy next) {
this.next = next;
}
}
给出测试程序:
/**
* 测试程序
*/
public static void main(String[] args) {
CircleSingleLinkedList circleSingleLinkedList = new CircleSingleLinkedList();
circleSingleLinkedList.addBoy(10);
circleSingleLinkedList.list();
circleSingleLinkedList.countBoy(2,2,10);
}