JS 雙向鏈表

雙向鏈表

鏈表有多種不同的類型话速,雙向鏈表和普通鏈表的區(qū)別在于,在鏈表中购公,一個節(jié)點(diǎn)只有鏈向下一個節(jié)點(diǎn)的鏈接萌京,而在雙向鏈表中,鏈接是雙向的:一個鏈向下一個元素宏浩,另一個鏈向前一個元素知残。

雙向鏈表

單鏈表的缺陷

單鏈表只能從頭節(jié)點(diǎn)開始訪問鏈表中的數(shù)據(jù)元素,如果需要逆序訪問單鏈表中的數(shù)據(jù)元素將極其低效比庄。

從鏈表的頭節(jié)點(diǎn)遍歷到尾節(jié)點(diǎn)很簡單求妹,但反過來從后向前遍歷則沒那么簡單。通過給Node對象增加一個屬性佳窑,該屬性存儲指向前驅(qū)節(jié)點(diǎn)的鏈接制恍,這樣就容易多了。此時神凑,向鏈表插入一個節(jié)點(diǎn)需更多的工作净神,需指出該節(jié)點(diǎn)正確的前驅(qū)和后驅(qū)。但從鏈表中刪除節(jié)點(diǎn)時溉委,效率提高了鹃唯,無需再查找待刪除節(jié)點(diǎn)的前驅(qū)節(jié)點(diǎn)。

雙向鏈表
function Node(element){
    this.element = element;
    this.prev = null;
    this.next = null;
}
function Llist(){
    this.head = new Node('head');
    this.find = find;
    this.insert = insert;
    this.display = display;
    this.remove = remove;
    this.findLast = findLast;
    this.dispReverse = dispReverse;
}

function find(item){
    var current = this.head;
    while(current.element!=item){
        current = current.next
    }
    return current;
}
//雙向鏈表的插入與單向鏈表類似瓣喊,但需設(shè)置新節(jié)點(diǎn)的prev屬性坡慌,使其指向該節(jié)點(diǎn)的前驅(qū)。
function insert(item,element){
    var current = this.find(item);
    var node = new Node(element);

    node.prev = current;
    node.next = current.next;
    current.next =  node;
}

// 雙向鏈表的刪除比單向鏈表效率更高藻三,因為不需要再查找前驅(qū)節(jié)點(diǎn)洪橘。
// 首先需要在鏈表中找出存儲帶刪除數(shù)據(jù)的節(jié)點(diǎn)絮爷,然后設(shè)置該節(jié)點(diǎn)前驅(qū)的next屬性,使其指向待刪除節(jié)點(diǎn)的后繼梨树。
// 設(shè)置該節(jié)點(diǎn)后繼的previous屬性,使其指向待刪除節(jié)點(diǎn)的前驅(qū)岖寞。
function remove(item){
    var current = this.find(item);
    if(!(current.next==null)){
        current.prev.next = current.next;
        current.next.prev = current.prev;
        current.next = null;
        current.prev = null;
    }
}
// 查找最后的節(jié)點(diǎn)抡四,同時避免從前往后遍歷鏈表。
function findLast(){
    var current = this.head;
    while(!(current.next==null)){
        current = current.next;
    }
    return current;
}
// 以反序顯示鏈表中元素
function dispReverse(){
    var current = this.head;
    current = this.findLast();
    while(!(current.prev == null)){
        print(current.element);
        current = current.prev;
    }
}

function display(){
    var current = this.head;
    while(!(current.next==null)){
        print(current.next.element)
        current = current.next;
    }
}
從雙向鏈表中刪除元素
var ll = new Llist();
ll.insert('head','conway');
ll.insert('conway','russellville');
ll.insert('russellville','alma');
ll.display();
print();

ll.remove('conway');
ll.display();
print();

ll.dispReverse();

雙向鏈表實現(xiàn)

雙向鏈表提供了兩種迭代列表的方法:從頭到尾仗谆,或反過來指巡。也可訪問一個特定節(jié)點(diǎn)的下一個或前一個元素。

在單向鏈表中隶垮,如果迭代列表時錯過要找的元素藻雪,就需要回到列表起點(diǎn),重新開始迭代狸吞。這就是雙向鏈表的一個優(yōu)點(diǎn)勉耀。

function DoublyLinkedList(){
    var Node = function(element){
        this.element = element;
        this.prev = null;
        this.next = null;
    };
    var length = 0;
    var head = null;
    var tail = null;
}

在任意位置插入新元素

  • 場景1:在鏈表起點(diǎn)插入新元素


    在鏈表起點(diǎn)插入元素

    若鏈表為空只需把head和tail都指向這個新節(jié)點(diǎn)。如果非空蹋偏,current變量將是對鏈表中第一個元素的引用便斥。就像在鏈表中所做的,將node.next設(shè)置為current威始,而head將指向node(它將成為列表中第一個元素)枢纠。不同之處在于,我們還需要為指向上一個元素的指針設(shè)置一個值黎棠。current.prev指針將由指向null變成指向新元素晋渺。node.prev指針已經(jīng)是null,因此無需再更新任何東西脓斩。

  • 場景2:在鏈表最后添加新元素


    在鏈表末尾插入元素

因為控制著指向最后一個元素的指針(tail)木西,current變量將引用最后一個元素。然后開始建立第一個鏈接:node.prev將引用current俭厚。current.next指針(指向null)將指向node(由于構(gòu)造函數(shù)户魏,node.next已經(jīng)指向了null)。最后是更新tail挪挤,它將由指向current變?yōu)橹赶騨ode叼丑。

  • 場景3:在鏈表中間插入新元素


    在鏈表中間插入新元素

迭代列表直到到達(dá)要找的位置,將在current和previous元素之間插入新元素扛门。首先鸠信,node.next將指向current,而previous.next將指向node论寨,這樣就不會丟失節(jié)點(diǎn)之間的鏈接星立。然后需要處理所有的鏈接current.prev將指向node爽茴,而node.prev將指向previous。

this.insert = function(position,element){
    if(position>=0 && position<=length){
        var node = new Node(element);
        var current = head;

        if(position==0){
            if(!head){
                head = node;
                tail = node;
            }else{
                node.next = current;
                current.prev = node;
                head = node;
            }
        }else if(position === length){
            current = tail;
            current.next = node;
            node.prev = current;
            tail = node;
        }else{
            var index = 0;
            var previous;
            while(index++ < position){
                previous = current;
                current = current.next;
            }
            node.next = current;
            previous.next = node;

            current.prev = node;
            node.prev = previous;
        }
        length++;
        return true;
    }else{
        return false;
    }
};

從任意位置移除元素

  • 場景1:從頭部移除一個元素
    current變量是對列表中第一個元素的引用绰垂,也就是想要移除的元素室奏。需要做的是改變head的引用,將其從current修改為下一個元素(current.next)劲装。但還需更新current.next指向上一個元素的指針胧沫,因為第一個元素的prev指針是null。因此占业,把head.prev的引用改為null - 因為head也指向列表中新的第一個元素绒怨,或者也可以用current.next.prev。由于還需控制tail的引用谦疾,可以檢查要移除的元素是否是第一個元素南蹂。若是則只需把tail設(shè)置為null。


    雙向鏈表移除第一個元素
  • 場景2:從最后一個位置移除元素
    既然已有對最后一個元素的引用(tail)念恍,就無需找到它而迭代列表六剥。可把tail的引用賦給current變量峰伙,把tail的引用更新為列表中倒數(shù)第二個元素(current.prev或tail.prev)仗考。既然tail指向倒數(shù)第二個元素,就只需把next指針更新為null(tail.next=null)词爬。


    從最后一個位置移除元素
  • 場景3:從列表中間移除一個元素
    首先需要迭代列表直到到達(dá)要找的位置秃嗜,current變量所引用的就是要移除的元素。要移除它可通過更新previous.next和current.next.prev的引用顿膨,在列表中跳過它锅锨。因此,previous.next將指向current.next恋沃,而current.next.prev將指向previous必搞。


    從列表中間移除一個元素
this.removeAt = function(position){
    //檢查越界值
    if(position>-1 && position<length){
        var current = head;
        //移除第一項
        if(position===0){
            head = current.next;
            //若只有一項則更新tail
            if(length===1){
                tail = null;
            }else{
                head.prev = null;
            }
        }else if(position === length-1){//移除最后一項
            current = tail;
            tail = current.prev;
            tail.next = null;
        }else{//移除中間相
            var index = 0;
            var previous;
            while(index++ < position){
                previous = current;
                current = current.next;
            }
            //將previous和current的下一項鏈接起來,跳過current囊咏。
            previous.next = current.next;
            current.next.prev = previous;
        }
        length--;
        return current.element;
    }else{
        return null;
    }
};
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末恕洲,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子梅割,更是在濱河造成了極大的恐慌霜第,老刑警劉巖,帶你破解...
    沈念sama閱讀 221,695評論 6 515
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件户辞,死亡現(xiàn)場離奇詭異泌类,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)底燎,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,569評論 3 399
  • 文/潘曉璐 我一進(jìn)店門刃榨,熙熙樓的掌柜王于貴愁眉苦臉地迎上來弹砚,“玉大人,你說我怎么就攤上這事枢希∽莱裕” “怎么了?”我有些...
    開封第一講書人閱讀 168,130評論 0 360
  • 文/不壞的土叔 我叫張陵苞轿,是天一觀的道長读存。 經(jīng)常有香客問我,道長呕屎,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 59,648評論 1 297
  • 正文 為了忘掉前任敬察,我火速辦了婚禮秀睛,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘莲祸。我一直安慰自己蹂安,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 68,655評論 6 397
  • 文/花漫 我一把揭開白布锐帜。 她就那樣靜靜地躺著田盈,像睡著了一般。 火紅的嫁衣襯著肌膚如雪缴阎。 梳的紋絲不亂的頭發(fā)上允瞧,一...
    開封第一講書人閱讀 52,268評論 1 309
  • 那天,我揣著相機(jī)與錄音蛮拔,去河邊找鬼述暂。 笑死,一個胖子當(dāng)著我的面吹牛建炫,可吹牛的內(nèi)容都是我干的畦韭。 我是一名探鬼主播,決...
    沈念sama閱讀 40,835評論 3 421
  • 文/蒼蘭香墨 我猛地睜開眼肛跌,長吁一口氣:“原來是場噩夢啊……” “哼艺配!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起衍慎,我...
    開封第一講書人閱讀 39,740評論 0 276
  • 序言:老撾萬榮一對情侶失蹤转唉,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后稳捆,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體酝掩,經(jīng)...
    沈念sama閱讀 46,286評論 1 318
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,375評論 3 340
  • 正文 我和宋清朗相戀三年眷柔,在試婚紗的時候發(fā)現(xiàn)自己被綠了期虾。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片原朝。...
    茶點(diǎn)故事閱讀 40,505評論 1 352
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖镶苞,靈堂內(nèi)的尸體忽然破棺而出喳坠,到底是詐尸還是另有隱情,我是刑警寧澤茂蚓,帶...
    沈念sama閱讀 36,185評論 5 350
  • 正文 年R本政府宣布壕鹉,位于F島的核電站,受9級特大地震影響聋涨,放射性物質(zhì)發(fā)生泄漏晾浴。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,873評論 3 333
  • 文/蒙蒙 一牍白、第九天 我趴在偏房一處隱蔽的房頂上張望脊凰。 院中可真熱鬧,春花似錦茂腥、人聲如沸狸涌。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,357評論 0 24
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽帕胆。三九已至,卻和暖如春般渡,著一層夾襖步出監(jiān)牢的瞬間懒豹,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,466評論 1 272
  • 我被黑心中介騙來泰國打工驯用, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留歼捐,地道東北人。 一個月前我還...
    沈念sama閱讀 48,921評論 3 376
  • 正文 我出身青樓晨汹,卻偏偏與公主長得像豹储,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子淘这,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,515評論 2 359

推薦閱讀更多精彩內(nèi)容