JavaScript數據結構與算法(鏈表)

去年4,5月份得時候看過Vue得源碼。沒記錯的話其中的Cache類應該就是用鏈表實現的. 雖然用得很少可是做爲數據結構的的重要組成部分,掌握它也是很是有必要的,下面主要以單鏈表進行說明.node

開始

爲了便於編寫和測試,首先設置幾個標準類數組

// 鏈表節點
class ListNode{
    constructor(val){
        this.val = val;
        this.next = null;
    }
}
// 將數組轉化爲鏈表
class chainList(arr){
    let head = new ListNode(arr[0]);
    let tail = head;
    for (let i = 1; i < arr.length; i++) {
        let node = new ListNode(arr[i]);
        tail.next = node;
        tail = node;
    }
    return head;
}
// 構建一個棧
const createStack = () => {
    class Stack{
        constructor(){
            this.top = 0;
            this.stores = [];
        }
        push(item){
            this.top++;
            return this.stores.push(item)
        }
        pop(){
            this.top--
            return this.stores.pop()
        }
        peer(){
            return this.stores[this.stores.length-1]
        }
        isEmpty(){
            return this.top == 0;
        }
    }
    return new Stack();
}
複製代碼

題目

要熟練的運用鏈表,最好仍是多作題bash

翻轉一個鏈表I

lintCode數據結構

樣例
給出一個鏈表1->2->3->null,這個翻轉後的鏈表爲3->2->1->null
挑戰
在原地一次翻轉完成
複製代碼

利用棧的先進後出來倒序dom

const reverse = function (head) {
    if (!head){
        return null;
    }
    let node = head;
    let stack = createStack();
    while (node != null) {
        stack.push(node);
        node = node.next;
    }
    let newHead = null, tail = null;
    while (!stack.isEmpty()) {
        let node = stack.pop();
        if (!newHead) {
            newHead = tail = node;
        }
        tail.next = node;
        tail = node;
    }
    tail.next = null;
    return newHead
}
複製代碼

翻轉一個鏈表II

lintCode測試

樣例
給出鏈表1->2->3->4->5->null, m = 2 和n = 4,返回1->4->3->2->5->null
挑戰
在原地一次翻轉完成
複製代碼

首先找到要倒序的起始節點和結束節點,而後利用棧的先進後出來倒序,最後將倒序以後的連接插入到原鏈表ui

const reverseBetween = function (head, m, n) {
    let node = head, stack = createStack();
    for (let i = 1; i < m-1; i++) {
        node = node.next;
    }
    let tail = null;
    if (m != 1) {
        tail = node;
        node = node.next;
    }
    for (let i = m; i <= n; i++) {
        stack.push(node)
        node = node.next;
    }
    while (!stack.isEmpty()) {
        let node = stack.pop();
        if (!tail) {
            tail = node;
            head=node
        }
        tail.next = node;
        tail = node;
    }
    tail.next = node;
    return head
}
複製代碼

鏈表求和 II

lintCodethis

樣例
給出 6->1->7 + 2->9->5。即,617 + 295。

返回 9->1->2。即,912 。
複製代碼

注意,這道題不能這麼作,617+295=912而後把912變成鏈表,由於個問題當鏈表足夠長時JS獲得得整型值會溢出。正確得解題是思路是對鏈表進行反轉,而後對應得節點進行求和以此來獲得最終得鏈表spa

const addLists2 = function (l1, l2) {
     let newL1 = reverse(l1);
    let newL2 = reverse(l2);
    let flag = 0, stack = createStack();

    while (newL1 && newL2) {
        let val1 = newL1.val;
        let val2 = newL2.val;
        let sum  = val1 + val2 + flag;
        if (sum >=10) {
            flag = 1;
            sum = sum-10
        } else {
            flag = 0;
        }
        stack.push(sum);
        newL1 = newL1.next;
        newL2 = newL2.next;
    }
    let reserve = newL1 ? newL1:newL2;
    if (reserve){
        reserve.val = reserve.val +flag;
    }else {
        if (flag) {
            stack.push(flag)
        }
    }
    while (reserve) {
        stack.push(reserve.val)
        reserve = reserve.next;
    }
    let head = null, tail = null;
    while (!stack.isEmpty()) {
        let node = new ListNode(stack.pop());
        if (!head) {
            tail = head = node;
        } else {
            tail.next = node;
            tail = node;
        }
    }
    return head;
}
複製代碼

合併兩個排序鏈表

lintCode指針

樣例
給出 1->3->8->11->15->null,2->null, 返回 1->2->3->8->11->15->null。
複製代碼

這道題很簡單,經過while循環比較二個連接得節點值 較小得放到新得鏈表中並日後移動一位,知道其中一個鏈表爲空

const mergeTwoLists = function (l1, l2) {
    let l = null, tail = null; 
    if (!l1) {
        return l2
    } else if (!l2) {
        return l1
    }
    while (l1 != null && l2 != null) {
        let node = null;
        if (l1.val < l2.val) {
            node = l1;
            l1 = l1.next;
        } else {
            node = l2;
            l2 = l2.next;
        }
        if (!l) {
            l = node;
            tail = node;
        } else {
            tail.next = node;
            tail = node;
        }
    }
    let remain = l1 ? l1 :l2;
    while (remain) {
        tail.next = remain;
        tail = remain;
        remain = remain.next
    }        
    return l
}
複製代碼

刪除鏈表中的元素

lintCode

樣例
給出鏈表 1->2->3->3->4->5->3, 和 val = 3, 你須要返回刪除3以後的鏈表:1->2->4->5
複製代碼

遇到須要刪除得值時,講前面一個節點得next設爲當前節點得next就好了

const removeElements = function (head, val) {
    let node = head, preNode = head;
    while (node) {
        if (node.val == val) {
            if (node == head && head) {
                head = head.next;
                node = head
                continue
            } else {
                preNode.next = node.next;
                 node = node.next;
                continue
            }
        }
        preNode = node;
        node = node.next;
    }
    return head
}
複製代碼

複製帶隨機指針的鏈表

lintCode

描述
給出一個鏈表,每一個節點包含一個額外增長的隨機指針能夠指向鏈表中的任何節點或空的節點。

返回一個深拷貝的鏈表。
複製代碼

這個題主要是隨機指針得處理,能夠經過hash進行映射找到隨機指針對應得節點

const copyRandomList = function (l1) {
   let l2 = null, tail2 = null, node1 = l1; hash = new Map();
   // 忽略random對l1進行復制
   while (node1) {
       if (!l2) {
           l2 = tail2 = node1
       } else {
           tail2.next = node1;
           tail2 = node1;
       }
       hash.set(node1, tail2);
       node1 = node1.next
   }
   while (l2 && l1) {
       l2.random = hash.get(l1.random) 
       l2 = l2.next;
   }
   return l2
}
複製代碼
相關文章
相關標籤/搜索