1. 程式人生 > >js建立單鏈表用於leedcode演算法題目-刪除連結串列的倒數第N個節點

js建立單鏈表用於leedcode演算法題目-刪除連結串列的倒數第N個節點

最近在做leedcode的連結串列題目時,非常頭疼,因為它提供的連結串列不能在我本地的機器上執行。所以我不得不先用js建立一個連結串列,然後才能做題。

連結串列科普:

陣列不總是最佳的資料結構,因為,在很多程式語言中,陣列的長度都是固定的,如果陣列已被資料填滿,再要加入新的元素是非常困難的。而且,對於陣列的刪除和新增操作,通常需要將陣列中的其他元素向前或者向後平移,這些操作也是十分繁瑣的。

然而,JS中陣列卻不存在上述問題,主要是因為他們被實現了成了物件,但是與其他語言相比(比如C或Java),那麼它的效率會低很多。

這時候,我們可以考慮使用連結串列(Linked-list) 來替代它,除了對資料的隨機訪問,連結串列幾乎可以在任何可以使用一維陣列的情況中。如果你正巧在使用C或者Java等高階語言,你會發現連結串列的表現要優於陣列很多。

連結串列其實有許多的種類:單向連結串列、雙向連結串列、單向迴圈連結串列和雙向迴圈連結串列,接下來,我們基於物件來實現一個單向連結串列,因為它的使用最為廣泛。

什麼是單鏈表?

連結串列是一個個節點組成的集合,每個節點都使用一個物件的引用來指向它的後一個節點。
在這裡插入圖片描述
其中,data儲存資料,next儲存著下一個連結串列的引用。上圖中,我們說 data2 跟在 data1 後面,而不是說 data2 是連結串列中的第二個元素。上圖,值得注意的是,我們將連結串列的尾元素指向了 null 節點,表示連結結束的位置。

由於連結串列的起始點的確定比較麻煩,因此很多連結串列的實現都會在連結串列的最前面新增一個特殊的節點,稱為 頭節點,表示連結串列的頭部。進過改造,連結串列就成了如下的樣子:
在這裡插入圖片描述


向連結串列中插入一個節點的效率很高,需要修改它前面的節點(前驅),使其指向新加入的節點,而將新節點指向原來前驅節點指向的節點即可。下面我將用圖片演示如何在 data2 節點 後面插入 data4 節點。
在這裡插入圖片描述
同樣,從連結串列中刪除一個節點,也很簡單。只需將待刪節點的前驅節點指向待刪節點的,同時將待刪節點指向null,那麼節點就刪除成功了。下面我們用圖片演示如何從連結串列中刪除 data4 節點。
在這裡插入圖片描述

單鏈表設計:

我們設計連結串列包含兩個類,一個是 Node 類用來表示節點,另一個是List 類提供插入節點、刪除節點等一些操作。

function Node(element) {
    this.element = element;//當前節點的元素
    this.next = null;//下一個節點連結
}
function List() {       
    this.head = new Node("head");//頭節點
    this.find = find;//查詢節點
    this.insert = insert;//插入節點
    this.remove = remove;//刪除節點
    this.display = display;//顯示連結串列
    this.findPrevious = findPrevious; //查詢前一個節點     
}

//下面的函式是操作方法:對應List類建構函式中的名稱
function find(item) {
    var currNode = this.head;
    while(currNode.element != item) {
        currNode = currNode.next;
    }
    return currNode;
}
function insert(newElement,item) {
    var newNode = new Node(newElement);
    var current = this.find(item);
    if(current == null) 
        return console.log("can't find the item");
    newNode.next = current.next;
    current.next = newNode;
}
function remove(item) {
    var prevNode = this.findPrevious(item);
    if(prevNode.next != null)
        prevNode.next = prevNode.next.next;
}
function findPrevious(item) {
    var currNode = this.head;
    while(currNode.next != null && currNode.next.element != item) {
        currNode = currNode.next;
    }
    return currNode;
}
function display() {
    var current = this.head;
    while(current.next != null) {
        console.log(current.next.element);
        current = current.next;
    }
}

測試:

假如我們想將1,2,3,4,5這五位數字寫進連結串列中

let list = new List();
list.insert(1,'head');
list.insert(2,1);
list.insert(3,2);
list.insert(4,3);
list.insert(5,4);
console.log(list.display());

在這裡插入圖片描述

好的,我們已經實現一個基本的單向連結串列了。接下來看題目:

給定一個連結串列,刪除連結串列的倒數第 n 個節點,並且返回連結串列的頭結點。

示例:
給定一個連結串列: 1->2->3->4->5, 和 n = 2.
當刪除了倒數第二個節點後,連結串列變為 1->2->3->5.

說明:

給定的 n 保證是有效的。

進階:

你能嘗試使用一趟掃描實現嗎?

/**
 * @param {ListNode} head
 * @param {number} n
 * @return {ListNode}
 */
var removeNthFromEnd = function(head, n) {
    let nodeToReturn = head;
    let pointer1 = head;
    let pointer2 = head;
    for(let i = 0; i<n;i++){
        pointer2 = pointer2.next;
    }
    if(!pointer2){
        return nodeToReturn.next;
    }
    while(pointer2.next){
        pointer1 = pointer1.next;
        pointer2 = pointer2.next;
    }  
    pointer1.next = pointer1.next.next;
    return nodeToReturn;
};
console.log(removeNthFromEnd(list.head,2))

執行後的結果
在這裡插入圖片描述