约瑟夫问题[韩顺平算法]
约瑟夫问题
在计算机编程的算法中,类似问题又称为约瑟夫环
约瑟夫环:N个人围成一圈,从第一个开始报数,第M个将被杀掉,最后剩下一个,其余人都将被杀掉。
构建单向的环形链表思路
- 首先创建第一个节点,并且该结点有first指针指向第一个结点,而这个结点的next域指向自己,从而形成环状
- 创建辅助结点curboy,用于指向第一个结点后的所有结点咯,辅助创建结点
- 每创建一个新的结点,就把该结点添加到已有的环形链表
遍历环形链表思路
- 创建临时指针curboy,先指向first结点
- 然后通过一个while循环遍历该环形链表即可curBoy == first结束
代码实例
package com.Yjm.josepfu;
/**
* @author 游锦民
* @version 1.0
*/
public class Josepfu {
public static void main(String[] args) {
//测试
//创建链表
RingSingleLinkedList linkedList = new RingSingleLinkedList();
linkedList.addBoy(5);
linkedList.showBoy();
}
}
/*
* 思路
* 1.创建环形链表
* 2.首先创建结点咯
* 3.然后再创建链表在里面创建结点
* */
//2.创建链表
class RingSingleLinkedList {
//单链表有什么内容?
//first结点
private Boy first = null;
//添加结点的方法
//添加的思路就是传进结点的个数然后通过遍历创建链表
public void addBoy(int num) {
if (num < 1) {
System.out.println("请输入正确的数量");
return;
}
//需要创建临时结点,用于辅助添加结点
Boy curBoy = null;
//使用循环创建环形链表
for (int i = 1; i <= num; i++) {
//根据序号创建结点
Boy boy = new Boy(i);
if (i == 1) {
//头指针指向第一个结点
first = boy;
//即使是单个,指针也要指向自己,构成环形
first.setNext(first);
//临时结点就是指向第一个结点的
curBoy = first;
} else {
//使用临时指针指向新结点
curBoy.setNext(boy);
//新结点指向first结点
boy.setNext(first);
//临时指针移到最新结点位置
curBoy = boy;
}
}
}
//变遍历当前链表
public void showBoy() {
//链表校验
if (first == null) {
System.out.println("什么都没有还遍历我? 你想要我命?");
return;
}
//遍历每一个结点
//创建临时结点
//临时结点指向第一个结点啊
Boy curBoy = first;
//通过循环遍历链表
while (true) {
//直接获取
//细节:不能获取next域
//报错的信息是:Exception in thread "main" java.lang.StackOverflowError
// 解析:栈溢出的意思。就是指对象之间相互引用,最终会导致栈溢出。
System.out.println("结点的编号 "+curBoy.getNo());
//判断全部遍历完毕
//当临时指针的next已经指向first就证明到最后了
if (curBoy.getNext() == first) {
break;
}
//获取前一个之后就遍历到后一个
curBoy = curBoy.getNext();
}
}
}
//1.创建结点类
class Boy {
//结点有序号
private int no;
//next指针用于指向下一个结点
private Boy next;
public Boy() {
}
public Boy(int no) {
this.no = no;
}
public Boy(int no, Boy next) {
this.no = no;
this.next = next;
}
/**
* 获取
*
* @return no
*/
public int getNo() {
return no;
}
/**
* 设置
*
* @param no
*/
public void setNo(int no) {
this.no = no;
}
/**
* 获取
*
* @return next
*/
public Boy getNext() {
return next;
}
/**
* 设置
*
* @param next
*/
public void setNext(Boy next) {
this.next = next;
}
public String toString() {
return "Boy{no = " + no + ", next = " + next + "}";
}
}
遇到问题
while (true) {
//直接获取
//细节:不能获取next域
//报错的信息是:Exception in thread "main" java.lang.StackOverflowError
// 解析:栈溢出的意思。就是指对象之间相互引用,最终会导致栈溢出。
System.out.println("结点的编号 "+curBoy);
解决: 异常----Exception in thread “main” java.lang.StackOverflowError_欧吉吉的博客-CSDN博客
约瑟夫问题实现
问题: 根据用户的输入,生成一个小孩出圈的顺序
n=5,即有5个人
k=2,从第2个人开始报数
m=2,数2下
实现思路思路:
1.创键一个辅助指针helper指向链表的最后一个结点
2.在小孩出圈前先把heleper和first指针移动到k-1个位置,(k就是决定位置)
3.1操作出圈,当小孩报数时候就让first指针和helper指针同时移动m-1次
3.2让first指针指向后一个,first=first.getNext(); 断开当前first的连接就会被垃圾回收机制回收
4.把helper指向的next域指向最新的first, helper.setNext(first);
代码实例
class RingSingleLinkedList {
//根据用户输入,计算出小孩出圈的顺序
//穿创建出圈方法
/*
*
* 问题: 根据用户的输入,生成一个小孩出圈的顺序
n=5,即有5个人
k=2,从第2个人开始报数
m=2,数2下
* 思路:
* 1.创键一个辅助指针helper指向链表的最后一个结点
* 2.在小孩出圈前先把heleper和first指针移动到k-1个位置,(k就是决定位置)
* 3.1操作出圈,当小孩报数时候就让first指针和helper指针同时移动m-1次
* 3.2让first指针指向后一个,first=first.getNext(); 断开当前first的连接就会被垃圾回收机制回收
* 4.把helper指向的next域指向最新的first, helper.setNext(first);
*
* */
/**
* @param startNo 表示开始的序号
* @param countNum 表示每次出圈的报数
* @param nums 总数
*/
public void countBoy(int startNo, int countNum, int nums) {
//思路:
//1.首先进行参数校验
if (first == null || startNo < 1 || countNum > nums) {
System.out.println("参数输入有误,请检查");
return;
}
//2.创建指向first的辅助指针,是指向而不是位于
//并且是位于最后一位
Boy helper = first;
while (true) {
//当hleper的后一个是first就证明到最后一个结点了
if (helper.getNext() == first) {
break;
}
//first指针是永远不动的
// first=first.getNext();
//helper已经获取到first结点的所有东西了,直接使用它后移就可以
helper = helper.getNext();
}
//出圈前要把first和helper指针移到starNo-1的位置
for (int i = 0; i < startNo - 1; i++) {
first = first.getNext();
helper = helper.getNext();
}
// 3.1操作出圈,当小孩报数时候就让first指针和helper指针同时移动countNum-1次
while (true) {
if (helper == first) {//说明圈中只有一个结点
break;
}
for (int i = 0; i < countNum - 1; i++) {
//移动到该位置就把该位置的first出圈
first = first.getNext();
helper = helper.getNext();
//把first出圈
System.out.println("出去号码:" + first.getNo());
first = first.getNext();
//把first前一个结点的指针指向最新的first即可
helper.setNext(first);
}
}
System.out.println("最后活下来的:" + first.getNo());
}
}