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儲存前一個節點, 主要邏輯分三步:
- 修改cur的next,指向前一個節點,即prev
- 儲存當前節點cur為prev,供下一個節點使用
- 移動當前節點指標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分別指向前三項,第一遍迴圈如下圖:
迴圈過程中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
};
複製程式碼
解題思路:
- 先用快慢指標方法確定連結串列是否有環
- 如果有環的,慢指標slow重新從head出發,slow,fast每次都走一步,如果重合,重合處必為環的入口;
入口解釋一下:
首先第一次遍歷後假設在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點相遇