Josephu, 约瑟夫, 约瑟夫环问题
设编号为1~n的n个人围坐到一圈, 约定编号为k(1<=k<=n)的人开始从1开始报数, 数到m的那个人出列, 它的下一位又开始从1开始数, 一次类推, 直到所有人都出列, 得到一个出列的序列.
分析
利用一个不带头结点的环形链表
创建一个不带头结点的环形链表
1.先创建一个节点, 并让frist指针指向该节点, 并形成环形
2.后面每新建一个新的节点, 就把该节点加入到已有的环形链表中去.
遍历环形链表
1.先让一个辅助指针cur指向第一个节点frist,
2.然后使用一个while循环遍历该环形链表,
3.直到当cur.next= frist结束.
解决问题的算法
1.需要创建一个last作为辅助变量, 实现指向最后一个节点, 当last== frist时就可以判断出是剩下最后一个
2.报数之前应先让frist和last同时移动k-1次, 到达一开始的报数位置
3.报数时, 让frist和last同时移动m-1次
4.这时frist指向的节点就可以出圈
5.frist指向下一个再开始报数, 出圈的这个节点没有被引用, 就会被回收
public class Josephu {
public static void main(String[] args) {
Ringsinglerlinkedlist rsll = new Ringsinglerlinkedlist();
rsll.create(5);
rsll.show();
rsll.showJosephu(1,2,5);
//rsll.show();
}
}
class Ringsinglerlinkedlist {
private Boy frist = null;
//传入一个int值, 创建一个int值个数的单向环形链表
public void create(int count){
if (count < 1){
System.out.println("节点的个数直少有一个");
return;
}
Boy cur = null;
for (int i = 1; i <= count; i++){
Boy boy = new Boy(i);
if (i == 1){
frist = boy;
frist.next = frist;
cur = frist;
}else{
cur.next = boy;
boy.next = frist;
cur = boy;
}
}
}
//遍历单向环形链表并打印
public void show(){
if (frist == null){
System.out.println("这个单向环形链表为空");
return;
}
Boy cur = frist;
while(true){
System.out.print(cur);
if (cur.next == frist){
break;
}
cur = cur.next;
}
System.out.println();
}
//约瑟夫问题的出列序列
public void showJosephu(int k, int m, int count){
if (k < 1 || k > count || frist == null){
System.out.println("链表是空或者开始位置有误~ ");
}
Boy last = frist;
while(true){
if (last.next == frist){
break;
}
last = last.next;
}
for (int i = 1; i<= k-1; i++){
frist = frist.next;
last = last.next;
}
while(true){
if (last == frist){
System.out.println(frist);
break;
}
for (int j = 1; j <= m-1; j++){
frist = frist.next;
last = last.next;
}
System.out.println(frist);
last.next = frist.next;
frist = frist.next;
}
}
}
class Boy{
public int number;
public Boy next;
public Boy(int number){
this.number = number;
}
@Override
public String toString() {
return "Boy{" +
"number=" + number +
'}';
}
}
package com.itdong.huiwei;
import java.util.ArrayList;
import java.util.List;
import java.util.Scanner;
public class YueSeFuHuan {
public static void main(String[] args) {
//获取n值,代表人数
Scanner scanner = new Scanner(System.in);
System.out.println("请输入人数:");
int n = scanner.nextInt();
//获取k值,代表从哪里开始
System.out.println("请输入开始开始游戏编号(1<=k<=n): ");
int k = scanner.nextInt();
//获取m值,代表游戏报数的数字
System.out.println("请输入报数的数字: ");
int m = scanner.nextInt();
//根据游戏人数n初始化环形链表
Boy boy = create(n);
//打印
show(boy);
//得到游戏结果list
List<Boy> play = play(boy, k, m);
//打印
System.out.println(play);
}
/**
* 根据头节点和开始k和计数m得到游戏的结果list
* @param head
* @param k
* @param m
* @return
*/
public static List<Boy> play(Boy head, int k, int m){
ArrayList<Boy> boys = new ArrayList<>();
Boy first = head;
Boy last = null;
Boy cur = first;
while(true){
if (cur.getNext() == first) {
last = cur;
break;
}
cur = cur.getNext();
}
//first和last移动k-1次,使得first到达开始计数的位置
for (int i = 1; i <= k-1; i++) {
first = first.getNext();
last = last.getNext();
}
//得到出环list
while(true){
if (first == last){
boys.add(first);
break;
}
for (int i = 1; i <= m-1; i++) {
first = first.getNext();
last = last.getNext();
}
//出环
boys.add(first);
last.setNext(first.getNext());
first = first.getNext();
}
return boys;
}
/**
* 根据头节点遍历环形链表
* @param boy
*/
public static void show(Boy boy){
Boy cur = boy;
Boy first = boy;
do{
System.out.println(cur);
cur = cur.getNext();
}while(cur != first);
}
/**
* 根据n创建环形链表并返回头节点
* @param n
* @return
*/
public static Boy create(int n){
Boy frist = null;
Boy cur = null;
for (int i = 0; i < n; i++) {
Boy boy = new Boy();
boy.setId(i+1);
if (i == 0){
frist = boy;
cur = boy;
}else if (i == (n-1)){
cur.setNext(boy);
boy.setNext(frist);
} else{
cur.setNext(boy);
cur = boy;
}
}
return frist;
}
}
class Boy{
private int id;
private Boy next;
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public Boy getNext() {
return next;
}
public void setNext(Boy next) {
this.next = next;
}
@Override
public String toString() {
return "Boy{" +
"id=" + id +
'}';
}
}