寫在前面: 我是 「揚帆向海」,這個暱稱來源於我的名字以及女朋友的名字。我熱愛技術、熱愛開源、熱愛編程。
技術是開源的、知識是共享的
。
這博客是對自己學習的一點點總結及記錄,如果您對 Java、算法 感興趣,可以關注我的動態,我們一起學習。
用知識改變命運,讓我們的家人過上更好的生活
。
相關文章
一、問題描述
約瑟夫問題(有時也稱爲約瑟夫斯置換,是一個出現在計算機科學和數學中的問題。在計算機編程的算法中,類似問題又稱爲約瑟夫環。又稱“丟手絹問題”。)
據說著名猶太歷史學家 Josephus有過以下的故事:在羅馬人佔領喬塔帕特後,39 個猶太人與Josephus及他的朋友躲到一個洞中,39個猶太人決定寧願死也不要被敵人抓到,於是決定了一個自殺方式,41個人排成一個圓圈,由第1個人開始報數,每報數到第3人該人就必須自殺,然後再由下一個重新報數,直到所有人都自殺身亡爲止。然而Josephus 和他的朋友並不想遵從。首先從一個人開始,越過k-2個人(因爲第一個人已經被越過),並殺掉第k個人。接着,再越過k-1個人,並殺掉第k個人。這個過程沿着圓圈一直進行,直到最終只剩下一個人留下,這個人就可以繼續活着。問題是,給定了和,一開始要站在什麼地方纔能避免被處決?Josephus要他的朋友先假裝遵從,他將朋友與自己安排在第16個與第31個位置,於是逃過了這場死亡遊戲。
二、問題分析
這個問題可以這樣來看,有N個人圍成一圈,第一個人從1開始報數,報到3的出圈被殺掉;下一個人接着從1開始報數… …這樣循環反覆,直到剩下最後兩個人,求出最後兩個人的位置。
三、實現邏輯
1. 構建一個單向循環鏈表(鏈表的尾部指向開頭)
① 首先創建循環鏈表的頭節點,讓head指向該節點,並形成環形;
② 之後每當創建一個新的節點,就把該節點添加到已有的環形鏈表中
2. 遍歷單向的循環鏈表
在此遍歷中,當有節點被刪除以後,就要向後移動節點。
注意:
噹噹前節點的值等於當前節點的下一個節點的值的時候,循環結束
四、代碼實現
package com.study.algorithm;
import java.util.Scanner;
/**
* @Description: 使用循環鏈表解決約瑟夫環問題
* @Author: 揚帆向海
* @Date: Created in 2020/5/2
*/
public class JosephCircle {
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
System.out.print("請輸入總人數N(N>=2):");
int n = scanner.nextInt();
if (n < 2) {
System.out.println("您好!請確保輸入的人數大於等於 2");
return;
}
// 構建鏈表並獲取頭節點,把頭節點賦值給currentNode
Node currentNode = buildData(n);
// 用來計數
int count = 0;
// 循環鏈表當前節點的上一個節點
Node beforeNode = null;
// 遍歷循環鏈表
while (currentNode != currentNode.next) {
count++;
if (count == 3) {
// 向後移動節點
beforeNode.next = currentNode.next;
System.out.println("出環的編號是: " + currentNode.data);
count = 0;
currentNode = currentNode.next;
} else { // 向後移動節點
beforeNode = currentNode;
currentNode = currentNode.next;
}
// 表示只有兩個節點了,不再進行出環操作
if (beforeNode.data == currentNode.next.data) {
break;// 跳出循環
}
}
// 輸出最後留在環中的編號
System.out.println("最後留在環中的編號是: " + currentNode.data + "," + currentNode.next.data);
}
/**
* 構建單向循環鏈表
*
* @param n 人數
* @return 返回頭節點
*/
private static Node buildData(int n) {
// 循環鏈表的頭節點
Node head = null;
// 循環鏈表當前節點的前一個節點
Node prev = null;
for (int i = 1; i <= n; i++) {
Node newNode = new Node(i);
// 如果是第一個節點
if (i == 1) {
head = newNode;
prev = head;
// 跳出當前循環,進行下一次循環
continue;
}
// 如果不是第一個節點
prev.next = newNode;
prev = newNode;
// 如果是最後一個節點
if (i == n) {
prev.next = head;
}
}
return head;
}
}
/**
* 鏈表節點
*/
class Node {
// 當前存儲的數據
int data;
// 當前節點的下一個節點
Node next;
public Node(int data) {
this.data = data;
}
}
測試結果:
請輸入總人數:41
出環的編號是: 3
出環的編號是: 6
出環的編號是: 9
出環的編號是: 12
出環的編號是: 15
出環的編號是: 18
出環的編號是: 21
出環的編號是: 24
出環的編號是: 27
出環的編號是: 30
出環的編號是: 33
出環的編號是: 36
出環的編號是: 39
出環的編號是: 1
出環的編號是: 5
出環的編號是: 10
出環的編號是: 14
出環的編號是: 19
出環的編號是: 23
出環的編號是: 28
出環的編號是: 32
出環的編號是: 37
出環的編號是: 41
出環的編號是: 7
出環的編號是: 13
出環的編號是: 20
出環的編號是: 26
出環的編號是: 34
出環的編號是: 40
出環的編號是: 8
出環的編號是: 17
出環的編號是: 29
出環的編號是: 38
出環的編號是: 11
出環的編號是: 25
出環的編號是: 2
出環的編號是: 22
出環的編號是: 4
出環的編號是: 35
最後留在環中的編號是: 16,31
由於水平有限,博客中難免會有一些錯誤,有紕漏之處懇請各位大佬不吝賜教!