用原生javascript來實現單向串列,實作了新增、查找、插入到特定位置、移除特定位置節點。
程式碼還有很多改進空間,就……放出來跟大家分享。
結構
會寫兩個類別-串列和節點。
先看節點
1 2 3 4 5 6 7
| class Node { constructor(data){ this.data = data; this.index = 0; this.next = null; } }
|
節點的結構很單純,data存放資料,index是這個節點所在位置,next則是指向下一個節點。
串列
1 2 3 4 5 6
| class LinkedList { constructor() { this.head = null; this.length = 0; } }
|
串列的結構也很單純,head是串列的起點,length為長度。
接下來介紹串列的方法,這些方法會接收節點作為參數,有時也會接收數字做為index
新增(append)
在串列的末尾新增節點。
思路:
新增節點有兩種狀況,一是若串列為空,那這個新增的節點就會是串列的起點;若是串列不為空,那就加在最後面。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| append(node){ this.length += 1;
if(!this.head){ this.head = node; } else {
let current = this.head; while(current.next){ current = current.next; } current.next = node; } this.redoIndex(); }
|
redoIndex 是一個重新計算節點index的函式
編號(index)
我其實不太確定這樣有沒有多此一舉,但是替節點編號會讓之後的插入和移除比較方便,所以我寫了一個給節點編index的函式。
思路:
在上一個函式中,有寫到遍歷節點的方式,這邊也運用到類似的技巧。
1 2 3 4 5 6 7 8 9 10 11
| redoIndex(){
let current = this.head; while(current.next){ current.next.index = current.index +1; current = current.next; } };
|
當串列的節點發生變化時,就要執行這個函式再度編號,因此名稱為redoIndex(詞窮)。
查找(findIndex)
既然將節點編號,那就要好好運用。findIndex會接收一個數字作為index,回傳該index的節點。
思路:
使用一樣的遍歷手法,從頭到尾檢查節點的index值,直到找到為止。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| findIndex(index){
if(index < 0 || index + 1 > this.length){ return new Error('not exist!'); } else { let current = this.head; while(current.next && current.index < index){ current = current.next; } return current; } };
|
插入到指定位置(insertAt)
insertAt會接收要插入位置的索引值,以及一個節點物件。
思路:
既然已經實做好根據index找尋節點的函式,就好好運用。
插入指定位置有兩種狀況,插入到第0位和非第0位,若是插入到第0位,就要改變head的值,指派給這個新節點,若是非0位,且不超過這個串列,就改變原本這個index所在位置的節點的next指向,以及該index前一位的next指向。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26
| insertAt(index, node){
if(index < 0 || index + 1 > this.length){ return new Error('not exist!'); }; if(index === 0){ node.next = this.head; this.head = node; this.length += 1; } else { this.length += 1; let current = this.findIndex(index); let prev = this.findIndex(index-1); console.log(`替換第${index}位`, current); node.next = current; prev.next = node; } this.redoIndex(); };
|
移除特定位置(removeAt)
removeAt會接受一個數字做為參數,代表要移除的位置。
思路:
其實跟插入滿像的,都是改變next這個指標。不過移除會有三種狀況,第一個是移除第0位,也就是起點,那就要重新指派起點;第二個是移除this.length -1
位,也就是移除尾巴,那麼尾巴前一位的節點next就應該變成是null,最後就是移除頭尾之間位置的節點,要改變該index及之前節點。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31
| removeAt(index){
if(index <0 || index + 1 > this.length){ return new Error('not exist!'); }
if(index === 0){ console.log('去首'); this.head = this.head.next; this.head.index = 0; } else if ( index === this.length -1) { console.log('去掉尾巴'); let prev = this.findIndex(index-1); prev.next = null; } else {
console.log('去掉第' + index + '位'); let current = this.findIndex(index); let prev = this.findIndex(index -1); prev.next = current.next; }; this.length -= 1; this.redoIndex(); }
|
後記
這周都在忙著找工作和面試的事情,除了體力上累心也好累,這系列可能會變weekly algo吧。
參考
用 JavaScript 學習資料結構和演算法:鏈結串列(Linked List)篇 - by kdchang