約瑟夫環的java解決


總共3中解決方法,1、數學推導,2、使用ArrayList遞歸解決,3、使用首位相連的LinkedList解決

import java.util.ArrayList;

/**
* 約瑟夫環問題
* 需求:n個人圍成一圈,從第一個人開始報數,數到K的人出局,然后從下一個人接着報數,直到最后一個人,求最后一個人的編號
* @author Miao
*
*/
public class Josephus {
public static void main(String[] args) {
int n = 13;
int k = 3;
int last;
// last =getLast(n,k); //使用數學推導求結果
/* 使用遞歸求結果
* ArrayList<Integer> list = new ArrayList<Integer>();
for (int i = 1; i <= n; i++) {
list.add(i);
}
last = getLast(list, k,0); //從下標為0開始報數,
*/
// System.out.println(last);
/**
* 使用鏈表方式求結果,鏈表為頭尾相接的環形鏈表
*/

MyLinkedList list = new MyLinkedList();
for (int i = 1; i <= n; i++) {
list.add(i);
}
//只要鏈表中元素個數大於1個,則指向下面步驟
while(list.length>1){
Node temp = list.first; //獲得頭節點
//將temp節點從頭節點向后挪k個位置
for (int i = 1; i < k; i++) {
temp = temp.next;
}

System.out.println("被移除的編號為:"+temp.value);
list.remove(temp); //移除當前temp位置的節點
System.out.println("剩余元素為:"+list.toString()+",長度為"+list.length);
}
//當上面循環結束時,鏈表內只剩1個元素
last = list.first.value;
System.out.println(last);
}
/**
* 數學推導,已知圈內只有一個人時候,剩余的是編號1,根據推導,如果當圈內有n-1個人時剩余的人編號為last,
* 那么當圈內有n人時,剩余的人的編號是last+k,因為可能last+k以后大於了當前圈內人數,所以剩余的
* 人的編號為(last+k)%n,但這時就會出現如果last+k后指向最后一個人,這時得到的編號為0,不符合要
* 求,所以用((last+k-1)%n)+1,這樣最后一個人報的數也是自己了,而其他位置的人報數不變。
*
* @param n 圈內人數
* @param k 數到該數字的人出局
* @return 剩余人的編號
*/
public static int getLast(int n , int k){

int last = 1; //這是初始值,即環內為1個人時剩余的人的編號,必然為1,也是后面推導的基礎
//System.out.println("當環內有1個人時,剩余的是:1");
for (int i = 2; i <= n; i++) { //當圈內為i個人時,結果是圈內為i-1個人的結果加K后對i求余
last = ((last + k - 1) % i) + 1; //為避免出現結果為0,讓i-1的結果先減1,求余后再加1
//System.out.println("當環內有" + i + "個人時,剩余的是:" + last);
}
return last;
}

/**
* 遞歸方式:第一輪根據傳入的集合、開始下標和間隔求出第一輪出局的人的下標,然后將該人移出集合,
* 並求出下一輪的開始下標,然后迭代調用本方法,將剩余集合元素和新的開始下標傳入計算,
* 直至剩余最后一個元素,就是最后存貨的元素
* @param list 傳入的集合
* @param k 報數到K的人出局
* @param m 從下標為m的人開始報數
* @return 剩余最后一個人的編號
*/
public static int getLast(ArrayList<Integer> list, int k , int m){
int last = -1; //用來放置最后一個人的編號
int index = -1; //用來放置當前一輪被移除的人的下標
if (list.size() == 1) { // 如果集合內只剩一個元素,則直接返回該元素作為結果
last = list.get(0);
} else {
index = (m + k - 1) % list.size(); // 求出當前一輪被移除的人的下標
/* System.out.println("當前集合內的元素為:" + list.toString());
System.out.println("從" + list.get(m) + "開始報數,被移除的是:"
+ list.get(index));*/
list.remove(index); // 將該人移除
m = index % list.size(); // 求出新一輪報數時開始的人在新集合里的下標
last = getLast(list, k, m); // 使用剩余的集合和m的位置重新開始下一輪計算
}
return last;
}

}

/**
* 定義一個雙向節點類,用作實現鏈表功能
* @author Miao
*
*/
class Node{
Integer value;
Node next;
Node prev;
public Node(){
value = null;
prev=null;
next = null;
}
public Node(Integer value){
this.value = value;
next = null;
prev = null;
}
public String toString(){
return this.prev.value+"<"+this.value+">"+this.next.value;
}
}
/**
* 定義自己的雙向鏈表類,用來放置數據
* @author Miao
*
*/
class MyLinkedList{
public Node first;
public Node last;
public int length;
public MyLinkedList(){
first = new Node();
last = first;
length = 0;
}
//在鏈表結尾增加元素的方法
public void add(Integer i){
if(length == 0){
first.value = i; //添加第一個元素,只需要設置該元素的value
}else{
Node temp = new Node(); //添加元素時,1、新建一個元素,
temp.value = i;
temp.prev = last; //2、然后先與last節點建立雙向關系,
last.next = temp;
first.prev = temp; //3、再與first節點建立雙向關系,
temp.next = first;
last = temp; //4、最后讓last指向該節點,3、4步可顛倒
}

length++;
}
//從鏈表中刪除指定節點的方法
public void remove(Node node){
node.prev.next = node.next; //將前節點的next跳過本節點,指向下個節點
node.next.prev = node.prev; //將后節點的prev跳過本節點,指向前節點 ,此時該節點已經從鏈表中移除了
this.first = node.next; //指定后節點為頭節點
this.last = node.prev; //指定前節點為尾節點
node = null;
length--;
}

public String toString(){
String str ="[";
Node temp = first;
for (int i = 1 ; i < length; i++ ) {
str += temp.value+",";
temp = temp.next;
}
str += (temp.value+"]");
return str;
}


}


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM