最近在看《數據結構與算法JavaScript描述》一書,在鏈表一章最後有個關於誰能倖存下來的習題比較有趣,現實中常常會有相似的場景,故將此題分享出來node
問題描述以下:算法
傳說在公元1世紀的猶太戰爭中,猶太曆史學家弗拉維奧•約瑟夫斯和他的40個同胞被羅馬士兵包圍。猶太士兵決定寧肯自殺也不作俘虜,因而商量出了一個自殺方案。他們圍成一個圈,從一我的開始,數到第三我的時將第三我的殺死,而後再數,直到殺光全部人。約瑟夫和另一我的決定不參加這個瘋狂的遊戲,他們快速地計算出了兩個位置,站在那裏得以倖存。寫一段程序將n我的圍成一圈,而且第m我的會被殺掉,計算一圈人中哪些人(m-1個)最後會存活。使用循環鏈表解決該問題。數組
咱們暫且就把由這n我的圍成,每次淘汰第m我的的動態圈稱爲約瑟夫環吧。數據結構
介紹解題思路前咱們先簡單瞭解下什麼是鏈表:post
鏈表是由一組節點組成的集合,每一個節點都使用一個對象的引用指向它的後繼。指向另外一個節點的引用叫作鏈。 下面介紹下鏈表相較於數組的有點,這也是此題爲何要用鏈表結構解題的緣由:學習
1.JavaScript中數組被實現成對象,與其餘語言的數組相比,效率很低;2.在數組中添加和刪除元素很麻煩,由於須要將數組中的其餘元素向前或向後平移 this
鏈表主要有如下三類:spa
由於n我的是圍成一圈,很明顯咱們要用到循環鏈表,因此咱們要先建立一個循環鏈表(注意除了這n我的外,須要添加一個Head節點指向第一我的,也方便咱們在遍歷節點時避免陷入死循環),每幹掉一我的就至關於從鏈表中刪除一個節點,由於每幹掉一我的後要從新從1開始數,因此須要將原先的Head節點移動至剛纔被幹掉的那我的的位置,就這樣每次都幹掉Head節點後的第m我的,如此循環,直至鏈表剩下的節點數小於m,此時人數不夠,遊戲結束。3d
注:本文不對鏈表的實現作詳細解釋,具體可參考學習JavaScript數據結構(二)——鏈表code
本題具體代碼以下:
//建立鏈表節點
function Node(name){
this.name=name;
this.next=null;
}
//鏈表類
function Ilist(){
this.head=new Node("head");
this.head.next=this.head;
this.find=find;
this.insert=insert;
this.display=display;
this.findPrevious=findPrevious;
this.remove=remove;
this.count=count;
this.back=back;
}
//根據節點內容找到當前節點
function find(ele){
var currentNode=this.head;
while(currentNode.name!=ele&&!(currentNode.next.name=="head")){
currentNode=currentNode.next;
}
return currentNode;
}
//根據節點內容找到當前節點的前一個節點
function findPrevious(ele){
var currentNode=this.head;
while((currentNode.next.name!=ele)&&!(currentNode.next==null)&&!(currentNode.next.name=="head")){
currentNode=currentNode.next;
}
return currentNode;
}
//在內容爲ele的節點後插入內容爲newEle的新節點
function insert(newEle,ele){
var node=new Node(newEle);
var oldNode=this.find(ele);
node.next=oldNode.next;
oldNode.next=node;
}
//刪除節點
function remove(ele){
var preNode=this.findPrevious(ele);
if(!(preNode.next==null)){
preNode.next=preNode.next.next;
}
}
//顯示鏈表全部節點的內容
function display(){
var currNode=this.head;
while(!(currNode.next==null)&&!(currNode.next.name=="head")){
console.log(currNode.next.name);
currNode=currNode.next;
}
}
//計算鏈表節點個數
function count(){
var qty=0;
var currNode=this.head;
while(!(currNode.next==null)&&!(currNode.next.name=="head")){
qty++;
currNode=currNode.next;
}
return qty;
}
//內容爲ele的節點向後移動step步
function back(ele,step){
var preNode=this.findPrevious(ele);
var currNode=preNode.next;
var prebackNode=currNode;
for(i=1;i<step;i++){
prebackNode=prebackNode.next;
}
var backnode=prebackNode.next;
preNode.next=currNode.next;
currNode.next=prebackNode.next.next;
backnode.next=currNode;
}
//計算最終的倖存者,manQty爲參加遊戲的人數,killNo爲被幹掉者編號
function survive(manQty,killNo){
var people=new Ilist();
for(i=1;i<=manQty;i++){
var person="位置"+i;
var prePerson;
if(i==1){
prePerson="head";
}else{
prePerson="位置"+(i-1);
}
people.insert(person,prePerson);
}
people.display();
console.log('遊戲總人數:'+people.count());
console.log('開始遊戲');
while(people.count()>=killNo){
var killedNode=people.head;
for(i=1;i<=killNo;i++){
killedNode=killedNode.next;
}
var killedPerson=killedNode.name;
var killedPreNode=people.findPrevious(killedPerson);
people.back("head",killNo);
people.remove(killedPerson);
console.log('幹掉:'+killedPerson);
console.log('還剩:'+people.count()+"人");
}
console.log('最終倖存'+people.count()+"人");
people.display();
}
複製代碼
調用方法survive(40,3)
,得出最終的倖存者所處位置: