1. 程式人生 > >js資料結構與演算法--雙向連結串列的實現

js資料結構與演算法--雙向連結串列的實現

雙向連結串列也叫雙鏈表,是連結串列的一種,它的每個資料節點中都有兩個指標,分別指向直接後繼和直接前驅。所以,雙向連結串列中的任意一個節點開始,都可以很方便的訪問它的前驅節點和後繼節點。

雙向連結串列結構圖

雙向連結串列的實現

linkednode.js ,裡面使用了類的繼承extends,使用了super函式。


/**

 * 連結串列節點,連結串列中的項,連結串列中的節點

 */

export class Node {

  constructor(element, next = null) {

    this.element = element // 連結串列中節點的值

    this.next = next // 指向列表中下一個節點項的指標

  }

}



export class DoublyNode extends Node {

  constructor(element, next = null, prev = null) {

    super(element, next)

    this.prev = prev

  }

}

doublyLinkedList.js 雙向連結串列類,實現了各個功能,功能說明,都在程式碼註釋中


import {

  DoublyNode

} from './linkednode'

/**

 * 雙向連結串列類

 */

export class DoublyLinkedList {

  constructor() {

    /**

     * 連結串列長度

     */

    this.length = 0

    /**

     * 頭指標

     */

    this.head = null

    /**

     * 尾指標

     */

    this.tail = null

  }

  /**

   * 在連結串列末尾新增元素

   * @param {*} element 需要插入的元素

   */

  append(element) {

    let node = new DoublyNode(element)

    if (!this.head) {

      this.head = node

      this.tail = node

    } else {

      this.tail.next = node

      node.prev = this.tail

      this.tail = node

    }

    this.length++

      return true

  }

  /**

   * 在任意位置插入元素

   * @param {Int32Array} position 指定位子

   * @param {*} element 需要插入的元素

   */

  insert(position, element) {

    // 檢查越界值

    if (position >= 0 && position <= this.length) {

      // 例項化一個雙向連結串列的節點

      let node = new DoublyNode(element)

      // 賦初始值

      let current = this.head

      let previous

      let index = 0 // 位置索引



      if (position === 0) { // 在第一個位子新增

        if (!this.head) { // 連結串列無資料的時候,將head和tail都指向新元素

          this.head = node

          this.tail = node

        } else { // 連結串列有資料的時候, head node current

          node.next = current

          current.prev = node

          this.head = node

        }

      } else if (position === this.length) { // 新增到最後一項 current node

        current = this.tail

        current.next = node

        node.prev = current

        this.tail = node

      } else { // 在列表中間位置新增

        // 新連結串列的節點原型是: previous <---> node <---> current

        while (index++ < position) { // 位置索引遞增到指定點之前,找出前後兩個節點

          previous = current // 當前節點設定為新連結串列中要插入的節點的前一個元素。

          current = current.next // 當前節點之後的元素設定為新連結串列中要插入的節點的當前元素

        }

        node.next = current

        previous.next = node



        current.prev = node

        node.prev = previous

      }

      this.length++ // 更新列表的長度

      return true

    } else {

      return false

    }

  }



  /**

   * 在任意位置插入元素

   * 在連結串列頭,在連結串列尾,在連結串列前半段,在連結串列後半段

   * @param {Int32Array} position 指定位置

   * @param {*} element 需要插入的元素

   */

  insert_up(position, element) {

    let node = new DoublyNode(element)

    let previous

    let current = this.head



    if (position > -1 && position <= this.length) {

      if (position === 0) {

        if (!this.head) {

          this.head = node

          this.tail = node

        } else {

          node.next = current

          current.prev = node

          this.head = node

        }

      } else if (position === this.length) {

        current = this.tail

        current.next = node

        node.prev = current

        this.tail = node

      } else if (position < this.length / 2) { // 目標在連結串列前半段

        let index = 0

        // 0 1 2 [] 3 4 5 

        while (index++ < position) {

          previous = current

          current = current.next

        }

        previous.next = node

        node.next = current



        node.prev = previous

        current.prev = node

      } else { // 目標在連結串列的後半段

        // 0 1 2 3 4 | 5 6 [] 7 8 9

        let index = this.length

        current = this.tail

        while (index-- > position) {

          previous = current.prev

          current = current

        }

        previous.next = node

        node.next = current



        node.prev = previous

        current.prev = node

      }

      this.length++

        return true

    } else {

      // 如果超出範圍,直接新增到連結串列末尾

      let current = this.tail

      current.next = node

      node.prev = current

      this.tail = node

      this.length++

        return true

    }

  }



  /**

   * 從任意位置移除元素,返回移除的元素

   * 從頭部,從尾部,從連結串列的前半段,從連結串列的後半段

   * @param {*} position 位置索引

   */

  removeAt(position) {

    let current = this.head // 當前項

    let previous // 前一項

    let index = 0 // 索引

    // 越界檢查

    if (position > -1 && position < this.length) {

      if (position === 0) { // 第一項

        this.head = current.next

        // 如果是最後一項要刪除,將tail置為null,此時head也為null

        // 如果非最後一項,則將this.head.prev置為null 

        if (this.length === 1) { // 只有一項的情況,更新tail

          this.tail = null

        } else {

          this.head.prev = null // 將首項的prev置空 或者 current.next.prev = null 

        }

      } else if (position === this.length - 1) { // 最後一項

        current = this.tail

        previous = current.prev

        this.tail = previous

        this.tail.next = null

      } else if (position <= this.length / 2) { // 索引在連結串列前半段,分開計算,提升效能

        while (index++ < position) {

          previous = current

          current = current.next

        }

        // 將previous與current下一項連起來---跳過current

        previous.next = current.next

        current.next.prev = previous

      } else { // 索引在連結串列後半段

        index = this.length - 1

        current = this.tail

        while (index-- > position) {

          previous = current

          current = current.prev

        }

        // 將previous與current的上一項連起來--跳過current

        previous.prev = current.prev

        current.prev.next = previous

      }

      this.length--

        return current.element

    } else {

      // 超出連結串列安全長度,連結串列有資料,則刪除末尾元素

      if (typeof position === 'number' && this.length > 0) {

        let current = this.tail

        this.tail = current.prev

        this.tail.next = null

        this.length--

          return current.element

      } else {

        return null

      }

    }



  }



  /**

   * 從列表中移除一項

   * 先找出元素的索引項,再根據索引移除元素

   * @param {*} element 列表中的元素

   */

  remove(element) {

    let index = this.indexOf(element)

    return this.removeAt(index)

  }



  /**

   * 返回元素在列表中的索引。如果列表中沒有該元素則返回-1

   * @param {*} element 元素

   */

  indexOf(element) {

    let current = this.head

    let index = 0 // 計算位置數

    while (current) {

      if (element === current.element) {

        return index

      }

      index++

      current = current.next

    }

    return -1

  }



  /**

   * 判斷是否為空連結串列

   * 空連結串列返回true,非空(連結串列長度大於0)返回false

   */

  isEmpty() {

    return this.size() === 0

  }



  /**

   * 返回連結串列包含的元素個數。與陣列的length屬性類似

   */

  size() {

    return this.length

  }



  /**

   * 獲取連結串列的表頭節點

   */

  getHead() {

    return this.head

  }



  /**

   * 獲取連結串列的尾節點

   */

  getTail() {

    return this.tail

  }

  /**

   * 輸出元素的值

   */

  toString() {

    let current = this.head

    let string = 'null'



    while (current) {

      string += "<--->" + current.element + (current.next ? '' : '<--->null')

      current = current.next

    }

    return string

  }

}

思考

雙向連結串列與單項鍊表的比較:

  1. 雙向連結串列可以雙向遍歷。從頭到尾,或者從尾到頭

  2. 雙向連結串列可以訪問一個特定節點的下一個或者前一個元素,而單鏈表只能訪問下一個元素。

  3. 雙向連結串列記憶體佔用比單鏈表的多

連結串列還有一個雙向迴圈連結串列,在需要用到的時候,考慮它們各自的不同,選擇合適的連結串列來操作。

相關推薦

js資料結構演算法--雙向連結串列實現

雙向連結串列也叫雙鏈表,是連結串列的一種,它的每個資料節點中都有兩個指標,分別指向直接後繼和直接前驅。所以,雙向連結串列中的任意一個節點開始,都可以很方便的訪問它的前驅節點和後繼節點。 雙向連結串列的實現 linkednode.js ,裡面使用了類的繼承extends,使用了super函式。 /**

資料結構演算法----雙向連結串列

PS:前面已經說過線性表的兩種表現形式,一種是順序,另一種是鏈式,鏈式的一種普通表現形式就是加入一個指標,前一個的指標指向後一個結點的地址,那麼還有一種形式就是雙向連結串列,裡面又加上了一個指標變數,讓前指標變數指向直接前驅,後指標變數指向直接後繼。 /** * 建立結構體 * */ typedef

資料結構演算法連結串列篇(上)

連結串列作為一種基礎的資料結構之一,我們會常常使用到它,接下來就讓我們一起學習吧。 1、連結串列的經典應用場景: LRU快取淘汰演算法。 2、快取是一種提高資料讀取效能的技術,在硬體設計、軟體開發中都有著非常廣泛的應用,比如常見的CPU快取、資料庫快取、瀏覽器快取等等。

資料結構演算法連結串列篇(下)

Q:如何輕鬆寫出正確的連結串列程式碼? 總結起來,就是投入時間+技巧; 一、投入時間:          只要願意投入時間,大多數人都是可以學會的,比如說,如果你真能花上一個週末或者一整天時間,就去寫連結

java版資料結構演算法—有序連結串列

package com.zoujc.sortLink; /** * 有序連結串列 */ class Link { public int dData; public Link next; public Link(int dd){ dData = d

資料結構演算法連結串列——遞增排序

今天看書時偶然想到的問題,書上是要求將一個數據插入一個有序連結的線性連結串列中, 所以我想先進行連結串列內的資料排序在進行插入資料。 在這裡我只寫了排序的函式。   函式實現: void Sort(LinkList&list, int &n) {   f

[一步步學資料結構演算法 07]-連結串列(下)

一、理解指標或引用的含義 1.含義:將某個變數(物件)賦值給指標(引用),實際上就是就是將這個變數(物件)的地址賦值給指標(引用)。 2.示例: p—>next = q; //表示p節點的後繼指標儲存了q節點的記憶體地址。 p—>next = p—&

【python資料結構演算法連結串列——連結串列中環的入口節點、兩個連結串列的第一個公共節點(相交連結串列

如題,這類問題在LeetCode上和劍指offer上總共有這些涉及: LeetCode:141,142,160 劍指offer:兩個連結串列的第一個公共節點(預設是無環單鏈表)、連結串列中環的入口節點 補充:兩個未知是否有環的單鏈表第一個公共節點 我直接敘述第三個問題,

資料結構演算法連結串列

    連結串列的分類:(1)單鏈表頭插法:只需要維護一個頭結點即可,常用來模擬堆疊;尾插法:需要維護頭結點和尾結點,常用來模擬佇列。(2)雙向連結串列雙向遍歷,可以用來儲存網頁的歷史記錄等;(3)迴圈連結串列經常出現在面試題中,判斷連結串列是否有環。連結串列的刪除方式一:維

資料結構演算法——有序連結串列合併

要求實現一個函式,將兩個連結串列表示的遞增整數序列合併為一個非遞減的整數序列。 #include <stdio.h> #include <stdlib.h> typedef int ElementType; typedef str

資料結構演算法連結串列(單鏈表、雙鏈表、環形連結串列

> 微信搜尋:碼農StayUp > 主頁地址:[https://gozhuyinglong.github.io](https://gozhuyinglong.github.io/) > 原始碼分享:[https://github.com/gozhuyinglong/blog-demos](h

資料結構演算法】——連結串列(Linked List)

## 連結串列(Linked List)介紹 > 連結串列是有序的列表,但是它在記憶體中是儲存如下: > > ![](http://itfxsen.oss-cn-chengdu.aliyuncs.com/%E6%95%B0%E6%8D%AE%E7%BB%93%E6%9E%84%E4%B8%8E%E7%AE%

js資料結構演算法--單鏈表的實現應用思考

連結串列是動態的資料結構,它的每個元素由一個儲存元素本身的節點和一個指向下一個元素的引用(也稱指標或連結)組成。 現實中,有一些連結串列的例子。 第一個就是尋寶的遊戲。你有一條線索,這條線索是指向尋找下一條線索的地點的指標。你順著這條連結去下一個地點,得到另一條指向下一處的線索。得到列表中間的線索的唯一辦法

js資料結構演算法--遞迴

遞迴,函式自己呼叫自己 return 返回值, 後面的程式碼不執行 function fn(num){ console.log(num) if(num == 0){ return;

javascript十大經典排序演算法 js資料結構演算法

排序演算法是《資料結構與演算法》中最基本的演算法之一 排序演算法中的複雜度與穩定性如下圖所示: 1.氣泡排序  氣泡排序(Bubble Sort)也是一種簡單直觀的排序演算法。它重複地走訪過要排序的數列,一次比較兩個元素,如果他們的順序錯誤就把他們交換過來。

資料結構 筆記:雙向連結串列實現

單鏈表的另一個缺陷 -單向性 ·只能從頭結點開始高效訪問連結串列中的資料元素 -缺陷 ·如果需要逆向訪問單鏈表中的資料元素將極其低效 雙向的線性表 -在“單鏈表”的結點中增加一個指標pre,用於指向當前結點的前驅結點 雙向連結串列的繼承層次結構 Dua

JS資料結構演算法 —— 集合,並集,交集,補集

概念:集合是由一組無序且唯一(每個元素只出現一次)的項組成的一組資料。其與數學裡的集合是同一個概念。在ES6裡已經引入了集合的資料結構概念——Set類。 分類:常見的有空集,並集,交集,差集。 應用場景:1)資料去重;2)用於儲存一些獨一無二的資料。 js實現一個集合 集合的特性

資料結構學習之雙向連結串列結構

注:本文的主要目的是為了記錄自己的學習過程,也方便與大家做交流。轉載請註明來自:         在前面總結的單向連結串列結構的基礎上,現在開始著手實踐實踐雙向連結串列結構,如果充分理解了單向連結串列資料結構,那對雙向連結串列結構的理解也就不再困難,換個角度而言,雙向連

資料結構】高效雙向連結串列list、樹tree(二叉樹)

vi正常模式下: "shift + g" 跳到最後一行 "gg" 跳到第一行 <效率更高的雙向連結串列結構程式碼>/*程式碼*/ 01link.c #include <stdlib.h> #include "01link.h" //連結串列初始化 v

js資料結構演算法之——陣列基本用法

陣列建立方式: var arr=new Array();                              var arr=new Array(10);                              var  arr=new Array(1,2,3,4,