leetcode:21. 合并两个有序链表
解题思路:
1、一个链表的算法题中是很常见的「虚拟头结点」技巧,也就是 dummy 节点
2、比较两个指针list1 和 list2 , 将值较小的的节点接到 cur 指针
var mergeTwoLists = function(list1, list2) {
let dummy = new ListNode()
let cur = dummy
while (list1 && list2) {
if (list1.val < list2.val) {
cur.next = list1
list1 = list1.next
} else {
cur.next = list2
list2 = list2.next
}
cur = cur.next
}
cur.next = list1 != null ? list1 : list2
return dummy.next
};
leetcode:86. 分隔链表
解题思路:
可以把原链表分成两个小链表,一个链表中的元素大小都小于
x
,另一个链表中的元素都大于等于x
,最后再把这两条链表接到一起就可以了。
var partition = function (head, x) {
let dummy1 = new ListNode() //存放小于x的链表的虚拟头结点
let dummy2 = new ListNode() //存放大于等于x的链表的虚拟头结点
let p1 = dummy1,
p2 = dummy2 // p1, p2 指针负责生成结果链表
let cur = head //cur负责遍历原链表,类似合并两个有序链表的逻辑
while (cur) {
if (cur.val >= x) {
p2.next = cur
p2 = p2.next
} else {
p1.next = cur
p1 = p1.next
}
let temp = cur.next // 断开原链表中的每个节点的 next 指针
cur.next = null
cur = temp
}
p1.next = dummy2.next //连接两个链表
return dummy1.next
};
leetcode:23. 合并K个升序链表 (常考)
解题思路:
- 遍历数组,合并两个成新的一个链表再继续合并下一个链表
var mergeKLists = function (lists) {
let ans = null;
//合并两个有序链表
var mergeTwoLists = function (list1, list2) {
let dummy = new ListNode()
let cur = dummy
while (list1 && list2) {
if (list1.val < list2.val) {
cur.next = list1
list1 = list1.next
} else {
cur.next = list2
list2 = list2.next
}
cur = cur.next
}
cur.next = list1 != null ? list1 : list2
return dummy.next
};
for (let k = 0; k < lists.length; k++) {
ans = mergeTwoLists(ans, lists[k])
}
return ans;
};
剑指 Offer 22. 链表中倒数第k个节点
解题思路:
- 先让一个指针
p1
指向链表的头节点head
,然后走k
步- 现在的
p1
,只要再走n - k
步,就能走到链表末尾的空指针了,再用一个指针p2
指向链表头节点head
- 让
p1
和p2
同时向前走,p1
走到链表末尾的空指针时前进了n - k
步,p2
也从head
开始前进了n - k
步,停留在第n - k + 1
个节点上,即恰好停链表的倒数第k
个节点上
var getKthFromEnd = function(head, k) {
let p1 = head
for(let i = 0 ; i< k ; i++){ //p1先走k步
p1 = p1.next
}
let p2 = head
while(p1){ //p1还剩n-k步,p1和p2同时走n-k步,当p1走完,p2刚好停在n-k+1步,即倒数第k步
p2 = p2.next
p1 = p1.next
}
return p2
};
leetcode:19. 删除链表的倒数第 N 个结点
解题思路:
要删除倒数第
n
个节点,就得获得倒数第n + 1
个节点的引用,可以用上面实现的getKthFromEnd
来操作。防止出现空指针的情况,使用虚拟节点
dummy
var removeNthFromEnd = function(head, n) {
var getKthFromEnd = function(head, k) {
let p1 = head
for(let i = 0 ; i< k ; i++){ //p1先走k步
p1 = p1.next
}
let p2 = head
while(p1){ //p1还剩n-k步,p1和p2同时走n-k步,当p1走完,p2刚好停在n-k+1步,即倒数第k步
p2 = p2.next
p1 = p1.next
}
return p2
};
let dummy = new ListNode() //虚拟头结点
dummy.next = head
let x = getKthFromEnd(dummy,n+1) // 删除倒数第 n 个,要先找倒数第 n + 1 个节点
x.next = x.next.next // 删掉倒数第 n 个节点
return dummy.next
};
leetcode:876. 链表的中间结点
解题思路:
- 让两个指针
slow
和fast
分别指向链表头结点head
。- 每当慢指针
slow
前进一步,快指针fast
就前进两步,这样,当fast
走到链表末尾时,slow
就指向了链表中点。- 如果有两个中间结点,则返回第二个中间结点。
- 这个思路可以用来做链表成环题
var middleNode = function(head) {
let slow = head, fast = head //初始化快慢指针
while (fast && fast.next) {
slow = slow.next //慢指针走一步
fast = fast.next.next //快指针走两步
}
return slow //快指针走完,慢指针刚好走到中点
};
leetcode:141. 环形链表
解题思路:
- 每当慢指针
slow
前进一步,快指针fast
就前进两步。- 如果
fast
最终遇到空指针,说明链表中没有环;如果fast
最终和slow
相遇,那肯定是fast
超过了slow
一圈,说明链表中含有环。
var hasCycle = function(head) {
let slow = head ,fast = head
while(fast && fast.next){
slow = slow.next
fast = fast.next.next
if(slow === fast) return true //相遇就说明有环
}
return false
};
leetcode:142. 环形链表 II
解题思路:
- 假设快慢指针相遇时,慢指针
slow
走了k
步,那么快指针fast
一定走了2k
步fast
一定比slow
多走了k
步- 假设相遇点距环的起点的距离为
m
,环的起点距头结点head
的距离为k - m
,也就是说如果从head
前进k - m
步就能到达环起点。- 只要把快慢指针中的任一个重新指向
head
,然后两个指针同速前进,k - m
步后一定会相遇,相遇之处就是环的起点了
var detectCycle = function (head) {
let slow = head,fast = head
while (fast && fast.next) {
slow = slow.next
fast = fast.next.next
if (fast == slow) break
}
if (!fast || !fast.next) return null //遇到空指针说明无环
slow = head //重新指向头结点,fast也行
while (slow != fast) { //同步前进,相交就是起点
fast = fast.next
slow = slow.next
}
return slow
};
leetcode:160. 相交链表
解题思路:
p1
遍历完链表A
之后开始遍历链表B
,p2
遍历完链表B
之后开始遍历链表A,
相当于「逻辑上」两条链表接在了一起。- 如果这样进行拼接,就可以让
p1
和p2
同时进入公共部分,也就是同时到达相交节点c1
:- 如下参考labuladong老师画的图:
var getIntersectionNode = function (headA, headB) {
let p1 = headA,
p2 = headB
while (p1 != p2) {
if (p1 == null) p1 = headB //p1走一步,如果headA走完就走到headB
else p1 = p1.next
if (p2 == null) p2 = headA //p2走一步,如果headB走完就走到headA
else p2 = p2.next
}
return p1
};