【數據結構與算法JavaScript實現與應用】鏈表 —— 倖存者遊戲(約瑟夫環)

前言

最近在看《數據結構與算法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),得出最終的倖存者所處位置:

順便再來看下鏈表結構
相關文章
相關標籤/搜索