leetcode題目解析(js)--連結串列

進擊的老兔發表於2019-03-03

206. 反轉連結串列

反轉一個單連結串列。

示例:

輸入: 1->2->3->4->5->NULL
輸出: 5->4->3->2->1->NULL

解法:

var reverseList = function(head) {
    let cur = head, prev = null
    while(cur) {
        [cur.next, prev, cur] = [prev, cur, cur.next]
    }
    return prev
};
複製程式碼

思路分析:prev儲存前一個節點, 主要邏輯分三步:

  1. 修改cur的next,指向前一個節點,即prev
  2. 儲存當前節點cur為prev,供下一個節點使用
  3. 移動當前節點指標cur到下一個節點cur.next

24. 兩兩交換連結串列中的節點

給定一個連結串列,兩兩交換其中相鄰的節點,並返回交換後的連結串列。 你不能只是單純的改變節點內部的值,而是需要實際的進行節點交換。

示例:

給定 1->2->3->4, 你應該返回 2->1->4->3.

解法一:

var swapPairs = function(head) {
    let node = new ListNode(0) //新建一個節點,用於儲存head節點
    node.next = head
    let prev = node
    while(prev.next && prev.next.next) {
        let a = prev.next
        let b = a.next
        prev.next = a.next
        a.next = b.next
        prev = b.next = a
    }
    return node.next
};
複製程式碼

解題思路: 定義一個新的頭節點node,將頭節點next指向head,定義prev, a, b分別指向前三項,第一遍迴圈如下圖:

leetcode題目解析(js)--連結串列
迴圈過程中node始終指向自定義的頭結點,而node.next即為真正的頭節點。
遍歷第二遍時,a, b分別指向3,4,執行流程同上,執行完第二遍即結束迴圈。

解法二:

var swapPairs = function(head) {
    if(!head) return null
    if(!head.next) return head
    let temp = head.next
    head.next = swapPairs(temp.next)
    temp.next = head
    return temp
};
複製程式碼

解題思路:主要邏輯是head.next指向遞迴返回的上一個temp,同時修改temp.next為head,實現後一個節點指向前一個節點,同時前一個節點指向遞迴返回的節點,最後swapPairs接收到的就是最後返回的temp,即第二個節點2.

141. 環形連結串列

給定一個連結串列,判斷連結串列中是否有環。

示例:

輸入:head = [3,2,0,-4], 輸出:true

解法一:使用快慢指標,快指標每次走兩步,慢指標每次走一步,如果有環,二者必定會碰到一起;

var hasCycle = function(head) {
    let fast = slow = head
    while(fast && slow && fast.next) { //fast.next不存在說明連結串列已走完
        slow = slow.next
        fast = fast.next.next
        if(fast === slow) {
            return true
        }
    }
    return false
};
複製程式碼

解法二:使用Set儲存每個節點的地址,如果有環,Set裡元素必定會重複;

var hasCycle = function(head) {
    let set = new Set()
    while(head) {
        if(set.has(head)) return true
        set.add(head)
        head = head.next
    }
    return false
};
複製程式碼

142. 環形連結串列 II

給定一個連結串列,返回連結串列開始入環的第一個節點。 如果連結串列無環,則返回 null

示例:

輸入:head = [3,2,0,-4]
輸出:tail connects to node index 1

解法一:

var detectCycle = function(head) {
    let set = new Set()
    let cur = head
    while(cur) {
        if(set.has(cur)) return cur
        set.add(cur)
        cur = cur.next
    }
    return null
};
複製程式碼

解題思路:用Set儲存每次遍歷的地址,遍歷時發現地址重複即為入口節點

解法二:

var detectCycle = function(head) {
    let fast = slow = head
    while(slow && fast && fast.next) {
        slow = slow.next
        fast = fast.next.next
        if(slow === fast) {
            slow = head
            while(slow !== fast) {
                slow = slow.next
                fast = fast.next
            }
            return fast
        }
    }
    return null
};
複製程式碼

解題思路:

  1. 先用快慢指標方法確定連結串列是否有環
  2. 如果有環的,慢指標slow重新從head出發,slow,fast每次都走一步,如果重合,重合處必為環的入口;

leetcode題目解析(js)--連結串列

入口解釋一下:

首先第一次遍歷後假設在C點相遇,此時:

慢指標走的S1 = |AB| + |BC|
快指標走的S2 = 2 * ( |AB| + |BC| )

在另一個角度,S2 走的距離就是 |AB| + |BC| + n * s (s為環的大小),即

快指標走的S2 = |AB| + |BC| + n * s

所以可以得到

2 * ( |AB| + |BC| ) = |AB| + |BC| + n * s
可以得到:|AB| = n * s - |BC|

可以看出如果slow從A點開始,fast從相遇點C開始,兩者以同樣速度必會在B點相遇

相關文章