鏈表

經常使用的鏈表結構

  • 單鏈表
    單鏈表
  • 雙向鏈表 空間換時間
    雙向鏈表
  • 循環鏈表
    循環鏈表
    鏈表的刪除/插入是O(1)級別的,可是隨機訪問須要O(n)的複雜度
  • 雙向循環鏈表
    雙向循環鏈表node

    技巧

  • 理解指針或引用的含義dom

將某個變量賦值給指針,實際上就是將這個變量的地址賦值給指針,或者反過來講,指針中存儲了這個變量的內存地址,指向了這個變量,經過指針就能找到這個變量。this

在編寫鏈表代碼的時候,咱們常常會有這樣的代碼:p->next=q。這行代碼是說,p 結點中的 next 指針存儲了 q 結點的內存地址。指針

還有一個更復雜的,也是咱們寫鏈表代碼常常會用到的:p->next=p->next->next。這行代碼表示,p 結點的 next 指針存儲了 p 結點的下下一個結點的內存地址。code

  • 警戒指針丟失和內存泄漏

注意不要把指針弄丟,好比插入是時候blog

插入

在刪除的時候要注意,釋放內存排序

  • 利用哨兵簡化實現難度

哨兵
在處理刪除時,head節點比較難處理,因此添加一個哨兵,而後處理完,返回哨兵的next遞歸

  • 重點處理邊界條件

常常用來檢查鏈表代碼是否正確的邊界條件有這樣幾個:內存

  1. 若是鏈表爲空時,代碼是否能正常工做?
  2. 若是鏈表只包含一個結點時,代碼是否能正常工做?
  3. 若是鏈表只包含兩個結點時,代碼是否能正常工做?
  4. 代碼邏輯在處理頭結點和尾結點的時候,是否能正常工做?
  • 舉例畫圖,輔助思考,多建指針

寫在紙上,把指針賦值和調整以後的結果也畫出來,對照寫,就比較簡單了
插入rem

練習題

共25個已完成13個,全是中等

個常見的鏈表操做:

  • 1.單鏈表反轉 206
    單鏈表反轉
/**
 * Definition for singly-linked list.
 * function ListNode(val) {
 *     this.val = val;
 *     this.next = null;
 * }
 */
/**
 * @param {ListNode} head
 * @return {ListNode}
 */

var reverseList = function(head) {
    var prev = null;
    var cur = head;
    var next = null;
    while (cur !== null) {
        next = cur.next;
        cur.next = prev;
        prev = cur;
        cur = next;
    }
    return prev;
}

// 鏈表的遞歸實現
var reverseList = function(H) {
    if (H === null || H.next === null)
        //鏈表爲空直接返回,而H->next爲空是遞歸基
        return H
    var newHead = reverseList(H.next) //一直循環到鏈尾
    H.next.next = H //翻轉鏈表的指向
    H.next = null //記得賦值NULL,防止鏈表錯亂
    return newHead //新鏈表頭永遠指向的是原鏈表的鏈尾
}
  • 2.鏈表中環的檢測 141
    快慢指針
使用set來存儲

var hasCycle = function(head) {
    const set = new Set();
    if (head == null) {
        return false;
    }
    while(head.next !== null) {
        if(set.has(head)) {
            return true;
        } else {
            set.add(head);
        }
        head = head.next;
    }
    return false;
};

使用快慢指針

var hasCycle = function(head) {
    if (head == null || head.next == null) {
        return false;
    }
    let slow = head;
    let fast = head.next;
    while (slow != fast) {
        if (fast == null || fast.next == null) {
            return false;
        }
        slow = slow.next;
        fast = fast.next.next;
    }
    return true;
};
  • 3.兩個有序鏈表合併 21
var mergeTwoLists = function(l1, l2) {
    let preHead = new ListNode();
    let cur = preHead;

    while (l1 && l2){
        if(l1.val <= l2.val){
            cur.next = new ListNode(l1.val);
            l1 = l1.next;
        }
        else {
            cur.next = new ListNode(l2.val);
            l2 = l2.next;
        }
        cur = cur.next;
    }
    cur.next = l1 || l2
    return preHead.next;
};
  • 4.刪除鏈表倒數第n個節點 19
var swapPairs = function(head) {
    var dummyHead = new ListNode(0);
    dummyHead.next = head;
    p = dummyHead;
    while(p.next && p.next.next) {
        var node1 = p.next;
        var node2 = p.next.next;
        var next = node2.next;
        
        node2.next = node1;
        node1.next = next;
        p.next = node2;
        
        p = node1
    }
    if (dummyHead.next === null) {
        return []
    }else {
        return dummyHead.next;
    }
    
};
  • 5.求鏈表的中間節點 876
    快慢指針
空間換時間  O(n) 空間複雜度:O(N)
var middleNode = function(head) {
    const arr = [];
    while(head !== null) {
        arr.push(head);
        head = head.next;
    }
    const middle = Math.floor(arr.length / 2);
    return arr[middle];   
};

快慢指針

var middleNode = function(head) {
    slow = fast = head;
    while (fast && fast.next) {
        slow = slow.next;
        fast = fast.next.next;
    }
    return slow;   
};
  • 6.兩兩交換鏈表中的節點 24 須要補充圖
var swapPairs = function(head) {
    var dummyHead = new ListNode(0);
    dummyHead.next = head;
    p = dummyHead;
    while(p.next && p.next.next) {
        var node1 = p.next;
        var node2 = p.next.next;
        var next = node2.next;
        
        node2.next = node1;
        node1.next = next;
        p.next = node2;
        
        p = node1
    }
    if (dummyHead.next === null) {
        return []
    }else {
        return dummyHead.next;
    }
    
};
  • 7.刪除鏈表中的節點 237
把下一個的值賦給這個node,而後刪除下一個節點便可

var deleteNode = function(node) {
  node.val = node.next.val;
  node.next = node.next.next;
};
  • 8.刪除排序鏈表中的重複元素 83
由於是有序的,因此直接前一個跟後一個對比刪除就好了時間複雜度O(N)
如何是無序的使用set也能夠作到O(N), 就是空間換時間

var deleteDuplicates = function(head) {
    if (head == null) {
        return [];
    }
    let cur = head;
    while(cur && cur.next){
        if (cur.val === cur.next.val) {
            cur.next = cur.next.next
        } else {
            cur = cur.next
        }
    }
    return head;
};
  • 9.移除鏈表元素 203
var removeElements = function(head, val) {
    while (head !== null && head.val === val) {
        head = head.next;
    }
    
    if (head === null) {
        return []
    }
    let cur = head;
    while(cur.next !== null) {
        if (cur.next.val === val) {
            cur.next = cur.next.next
        } else {
            cur = cur.next;
        }
    }
    return head;
};

<!--遞歸-->
if (head == null){ 
    return null;
    } 
head.next = removeElements(head.next,val); 
return head.val == val ? head.next:head;
    
// 使用頭部守位   而後返回 dummnyHead.next
    
var removeElements = function(head, val) {
    let dummnyHead = new ListNode(0);
    dummnyHead.next = head;
    let cur = dummnyHead;
    while(cur.next !== null) {
        if (cur.next.val === val) {
            cur.next = cur.next.next
        } else {
            cur = cur.next;
        }
    }
    if (dummnyHead.next === null){
        return []
    } else {
       return dummnyHead.next
    }
};
  • 10.迴文鏈表 234
快慢針找到中間,而後把後面的翻轉,而後對比
var reverseList = function(head) {
    var pre = null;
    var cur = head;
    var next = null;
    while(cur !== null) {
        next = cur.next;
        
        cur.next = pre;
        
        pre = cur;
        cur = next;
    }
    return pre;
}

var isPalindrome = function(head) {
    if(head === null || head.next === null) {
        return true;
    }
    fast = slow = head;
    while (fast && fast.next) {
        slow = slow.next;
        fast = fast.next.next;
    }
    if(fast) slow = slow.next;
    let list2 = reverseList(slow);
    while(list2 !== null) {
        if (head.val !== list2.val) {
            return false;
        }
        head = head.next;
        list2 = list2.next;
    }
    return true;
};
  • 11.旋轉鏈表 61
  • 12.相交鏈表 160
先變量兩個表,而後把長度磨平,而後在對比節點是否相等
var getIntersectionNode = function(headA, headB) {
    if(headA === null || headB === null) {
        return null;
    }
    var listA = headA;
    var listB = headB;
    
    var lenA = 0;
    var lenB = 0;
    while(listA !== null) {
        listA = listA.next;
        lenA++;
    }
    while(listB !== null) {
        listB = listB.next;
        lenB++;
    }
    if (lenA > lenB) {
        var diff = lenA - lenB;
        while(diff>0) {
            headA = headA.next;
            diff--;
        }
    }
    if (lenB > lenA) {
        var diff = lenB - lenA;
        while(diff>0) {
            headB = headB.next;
            diff--;
        }
    }
    
    while(headB!=null) {
        if(headA==headB)
            return headA;
        headA=headA.next;
        headB=headB.next;
    }
    return null;
};
  • 13.排序鏈表 148 https://www.youtube.com/watch?v=M1TwY0nsTZA
  • 14.分隔鏈表 86
  • 15.鏈表隨機節點 382 https://www.youtube.com/watch?v=oCKJXWpgFm4
public class Solution {
    
    ListNode head;
    Random random;
    
    public Solution(ListNode h) {
        head = h;       
        random = new Random();        
    }
    
    public int getRandom() {
        
        ListNode c = head;
        int r = c.val;
        for(int i=1;c.next != null;i++){
            
            c = c.next;
            if(random.nextInt(i + 1) == i) r = c.val;                        
        }
        
        return r;
    }
}
  • 16.鏈表組件 817 https://www.youtube.com/watch?v=y1_lFSkQNDc
  • 17.最長數對鏈 646
  • 18.重排鏈表 143
  • 19.分隔鏈表 725 https://www.youtube.com/watch?v=fk8JTWhM-4U
var len = 0, curr = root;
    while (curr) {
        len++;
        curr = curr.next;
    }
    var quo = Math.floor(len / k);
    var rem = len % k;
    var results = [root];
    curr = root;
    var last = curr;

    while (k > 1) {
        for (var i = 0; i < quo; i++) {
            last = curr;
            curr = curr.next;
        }
        if (rem > 0) {
            last = curr;
            curr = curr.next;
            rem--;
        }
        if (last) {
            last.next = null;    
        }
        results.push(curr);
        k--;
    }
    return results.map(item => item === null ? []: item);

先遍歷一遍,拿到長度,而後len / k 就是要分紅幾個,而後len % k 就是有一個是多1的

[1,2,3,4,5,6,7,8,9,10]
===>
[4,3,3]

  • 20.刪除排序鏈表中的重複元素 83
var deleteDuplicates = function(head) {
    if (head == null) {
        return [];
    }
    let cur = head;
    while(cur && cur.next){
        if (cur.val === cur.next.val) {
            cur.next = cur.next.next
        } else {
            cur = cur.next
        }
    }
    return head;
};
  • 21.奇偶鏈表 328
  • 22.兩數相加 2
var addTwoNumbers = function(l1, l2) {
    if (!l1) return l2
    if (!l2) return l1
    let p1 = l1
    let p2 = l2
    let val, carry
    const ret = new ListNode()
    let cur = ret
    while (p1 || p2) {
        let current = add(p1, p2, carry)
        cur.val = current.val
        carry = current.carry
        if (p1) p1 = p1.next
        if (p2) p2 = p2.next
        if (p1 || p2) {
            cur.next = new ListNode()
            cur = cur.next
        }
    }
    if (carry) {
        cur.next = new ListNode(carry)
    }
    return ret
};

function add(p1, p2, _carry = 0) {
    let val = ((p1 && p1.val) || 0) + ((p2 && p2.val) || 0) + _carry
    let carry = (val >= 10) ? 1 : 0
    if (carry) { val -= 10 }
    return { val, carry }
}
  • 23.刪除排序鏈表中的重複元素 II82
  • 24.k個一組翻轉鏈表 25
相關文章
相關標籤/搜索