既然已經有陣列了,為什麼還要連結串列?JS連結串列(Linked-list)詳解

前端有貓膩發表於2022-02-15

海闊憑魚躍,天高任鳥飛。Hey 你好!我是秦愛德。?

之前看過這樣一個問題“既然已經有陣列了,為什麼還要連結串列?”

其實連結串列和陣列各有千秋,都在不同的業務場景中發光發熱,很多同學對連結串列可能是既熟悉又陌生。熟悉的是,我們在刷一些八股文的時候經常會看到“連結串列”這個字眼,陌生的是,我們在平時的開發中並不會太多的使用到連結串列。

那麼我們就來帶著問題了解一下啥是連結串列,既然已經有陣列了,為什麼還要連結串列?

資料結構的概念?

我們來把看起來晦澀難懂的專業術語拆分一下:

資料:對應的就是資料型別,在js中包含了基本資料型別引用資料型別

結構:將一堆各種各樣的資料按照不同的邏輯排列組合最終儲存到計算機記憶體當中

總結:我們把資料的各種邏輯組成,在計算機中的儲存結構以及各種操作的演算法設計叫做資料結構

演算法和資料結構的關係

演算法是建立在資料結構之上,對資料結構的操作需要用演算法來描述;演算法設計依賴資料的邏輯結構,演算法的實現依賴資料的儲存結構

  • 常見的資料結構

陣列、字典、連結串列、棧、佇列、雜湊表、二叉樹、堆、跳錶、圖、Trie樹

  • 常見的演算法

遞迴、排序、二分查詢、搜尋、雜湊演算法、貪心演算法、分治演算法、回溯演算法、動態規劃、字串匹配演算法等

什麼是陣列結構

1. 陣列的定義

陣列是若干個元素按照順序排列存放的一個集合,並且每個元素至少存在一個 索引(index) 或者 關鍵字(key) 所標識,每個元素的位置都可以通過計算索引拿到

為什麼說每個元素至少有一個索引呢,那是因為陣列可以是多維度的

但是我們在日常工作中一般常用的是一維陣列,也可以稱之為 線性陣列

image.png

一維陣列:[1,2,3];  //陣列的每一個元素是一個資料型別

二維陣列:[["a","b","c"],[1,2,3],123];  //陣列的每一個元素是一個一維陣列

三維陣列:[[["a","b","c"],[1,2,3]],[["a","b","c"],[1,2,3]]];  //陣列的每一個元素是一個二維陣列

2. 陣列的優缺點

陣列作為我們工作中最為常見的一種資料結構,其最大的特性莫過於高效的 查詢 資料

但是其缺點也是非常的明顯,在進行 插入刪除 資料時,需要進行大量的資料移動補位消耗大量的時間

image.png

什麼是連結串列結構

1. 連結串列的定義

連結串列結構其實是記憶體內部的一種儲存方式,連結串列則是把一系列節點串聯起來,每個節點上至少包含兩個部分: 資料域指標域

資料:儲存資料

指標:指向下一個節點的引用

連結串列中的每個節點,通過指標域的值,形成一個線性結構

image.png

2. 連結串列的優缺點

因為連結串列是一種 鬆散 的結構體,所以當你想要找到其中的某一個節點時,只能夠從 頭節點 一級一級的往下找,但也因為這種鬆散的結構使得其進行 插入刪除 時只需要改變其 指標域 的指向即可

優點:適合動態插入和刪除的應用場景 缺點:不能快速的定位和隨機訪問資料

陣列和連結串列的對比總結

  • 陣列和連結串列都是線性資料結構
  • 陣列為靜態結構,靜態分配記憶體。連結串列支援動態分配記憶體
  • 陣列在資料儲存時是一段連續的記憶體空間,連結串列是非連續的通過指標來串聯
  • 陣列可以根據下標定位快速查詢,連結串列則需要遍歷查詢
  • 陣列在插入和刪除時會有大量的資料移動補位,連結串列只需要改變指標指向

js中連結串列的實現

不同於new Array()new Set()new Map()等資料結構,目前js官方還沒有為我們提供一個直接的連結串列API實現。不過我們可以通過物件的方式去模擬出一個連結串列

連結串列可以分為三類:

  • 單向連結串列:線型資料結構,指標指向下一個節點,終點指向null
  • 雙向連結串列:可以往前或者往後新增節點,指標指向前一個節點和後一個節點
  • 迴圈連結串列:迴圈連結串列的第一個節點指向最後一個節點,最後一個節點指向第一個節點(迴圈連結串列又可以劃分為“單向迴圈連結串列”和“雙向迴圈連結串列”)

物件化連結串列之後,連結串列的呈現是這個樣子

// 連結串列物件化,便於理解
const obj = {
  data: 1,
  next: {
    data: 2,
    next: {
      data: 3,
      next: null,
    },
  },
};

連結串列的插入

當我們需要向連結串列中插入一個節點時,只需要將需要插入地方的 上一個節點 指向自己,並且將 當前節點 指向下一個節點就完成了

連結串列的刪除

當我們想要刪除連結串列中一個節點時,只需要將目標節點的 上一個節點 指向當前節點的 下一個節點 ,並且將目標節點指向到null完成釋放,就可以完成一個刪除操作

實現一個單向連結串列

class neNode {
  constructor(data) {
    this.data = data;
    this.next = null;
  }
}

// 實現一個單項鍊表
class singleLinkedList {
  constructor() {
    this.head = null;
  }
  // 新增節點
  add(data) {
    let node = new neNode(data);
    if (this.head === null) {
      this.head = node;
    } else {
      let current = this.head;
      while (current.next) {
        current = current.next;
      }
      current.next = node;
    }
  }
  // 插入節點
  insert(data, target) {
    let node = new neNode(data);
    let current = this.head;
    while (current.next) {
      if (current.data === target) {
        node.next = current.next;
        current.next = node;
        break;
      }
      current = current.next;
    }
  }
  // 查詢節點
  find(data) {
    let current = this.head;
    while (current) {
      if (current.data === data) {
        return current;
      }
      current = current.next;
    }
    return null;
  }
  // 刪除節點
  remove(data) {
    let current = this.head;
    let previous = null;
    while (current) {
      if (current.data === data) {
        if (previous === null) {
          this.head = current.next;
        } else {
          previous.next = current.next;
        }
        return true;
      }
      previous = current;
      current = current.next;
    }
    return false;
  }
}
const list = new singleLinkedList();
list.add(1);
list.add(2);
list.add(3);
list.insert(4, 2);
console.dir(list, { depth: null });

列印結果為:

image.png

感謝

歡迎關注我的個人公眾號前端有貓膩每天給你推送新鮮的優質好文。回覆 “福利” 即可獲得我精心準備的前端知識大禮包。願你一路前行,眼裡有光!

感興趣的小夥伴還可以加我微信:yuyue540880拉你進群,一起交流前端技術,一起玩耍!

相關文章