21.合并两个有序列表
输入:l1 = [1,2,4], l2 = [1,3,4]
输出:[1,1,2,3,4,4]
采用一个空节点,没什么好说的。
class Solution:
def mergeTwoLists(self, l1: ListNode, l2: ListNode) -> ListNode:
dummy = ListNode()
p = dummy
while l1 and l2:
if l1.val < l2.val:
dummy.next = l1
dummy = dummy.next
l1 = l1.next
else:
dummy.next = l2
dummy = dummy.next
l2 = l2.next
while l1:
dummy.next = l1
dummy = dummy.next
l1 = l1.next
while l2:
dummy.next = l2
dummy = dummy.next
l2 = l2.next
return p.next
23. 合并K个升序列表
输入:lists = [[1,4,5],[1,3,4],[2,6]]
输出:[1,1,2,3,4,4,5,6]
class Solution:
def mergeKLists(self, lists: List[ListNode]) -> ListNode:
import heapq
heapMin = []
for ls in lists:
while ls:
heapq.heappush(heapMin, ls.val)
ls = ls.next
dummy = ListNode(0)
p = dummy
while heapMin:
p.next = ListNode(heapq.heappop(heapMin))
p = p.next
return dummy.next
使用最小堆,将所有列表的所有元素进行排序,然后依次放入链表中即可。
堆的使用:
import heapq
heapq.heappush(heapMin, ls.val)
heapq.heappop(heapMin)
19.删除链表的倒数第N个节点
输入:head = [1,2,3,4,5], n = 2
输出:[1,2,3,5]
尝试使用一次扫描实现。
可以先让p1走k步,现在的p1只需要再走n-k步就能走到链表末尾。这时p2指向头节点,往后走,当p1走到末尾,p2就走到了倒数第k个节点。
这样就得到了倒数第k个节点。
class Solution:
def removeNthFromEnd(self, head: ListNode, n: int) -> ListNode:
def findFromEnd(head: ListNode, k: int):
p1 = head
while k:
p1 = p1.next
k = k - 1
p2 = head
while p1:
p2 = p2.next
p1 = p1.next
return p2
dummy = ListNode(0)
dummy.next = head
x = findFromEnd(dummy, n+1)
x.next = x.next.next
return dummy.next
不过注意我们又使用了虚拟头结点的技巧,也是为了防止出现空指针的情况,比如说链表总共有 5 个节点,题目就让你删除倒数第 5 个节点,也就是第一个节点,那按照算法逻辑,应该首先找到倒数第 6 个节点。但第一个节点前面已经没有节点了,这就会出错。
141.环形链表 142.环形链表II
每当慢指针slow前进一步,快指针fast就前进两步。
如果fast最终遇到空指针,说明链表中没有环;如果fast最终和slow相遇,那肯定是fast超过了slow一圈,说明链表中含有环。
可以看到,当快慢指针相遇时,让其中任一个指针指向头节点,然后让它俩以相同速度前进,再次相遇时所在的节点位置就是环开始的位置。
我们假设快慢指针相遇时,慢指针slow走了k步,那么快指针fast一定走了2k步.
fast一定比slow多走了k步,这多走的k步其实就是fast指针在环里转圈圈,所以k的值就是环长度的「整数倍」。
假设相遇点距环的起点的距离为m,那么结合上图的 slow 指针,环的起点距头结点head的距离为k - m,也就是说如果从head前进k - m步就能到达环起点。
巧的是,如果从相遇点继续前进k - m步,也恰好到达环起点。因为结合上图的 fast 指针,从相遇点开始走k步可以转回到相遇点,那走k - m步肯定就走到环起点了:
所以,只要我们把快慢指针中的任一个重新指向head,然后两个指针同速前进,k - m步后一定会相遇,相遇之处就是环的起点了。
def detectCycle(self, head: ListNode) -> ListNode:
slow = head
fast = head
while fast and fast.next:
slow = slow.next
fast = fast.next.next
if fast == slow:
break
if fast == None or fast.next== None:
# 说明没有环
return None
# 重新指向头节点
slow = head
# 快慢指针同步,相交点就是环起点
while slow != fast:
fast = fast.next
slow = slow.next
return slow
876. 链表的中间节点
每当慢指针slow前进一步,快指针fast就前进两步,这样,当fast走到链表末尾时,slow就指向了链表中点。
class Solution:
def middleNode(self, head: ListNode) -> ListNode:
slow = head
fast = head
while fast and fast.next:
slow = slow.next
fast = fast.next.next
return slow
160. 相交链表
给你输入两个链表的头结点headA和headB,这两个链表可能存在相交。
如果相交,你的算法应该返回相交的那个节点;如果没相交,则返回 null。
所以,我们可以让p1遍历完链表A之后开始遍历链表B,让p2遍历完链表B之后开始遍历链表A,这样相当于「逻辑上」两条链表接在了一起。
如果这样进行拼接,就可以让p1和p2同时进入公共部分,也就是同时到达相交节点c1:
那你可能会问,如果说两个链表没有相交点,是否能够正确的返回 null 呢?
这个逻辑可以覆盖这种情况的,相当于c1节点是 null 空指针嘛,可以正确返回 null。
class Solution:
def getIntersectionNode(self, headA: ListNode, headB: ListNode) -> ListNode:
p1 = headA
p2 = headB
while p1!= p2:
if p1 == None:
p1 = headB
else:
p1 = p1.next
if p2 == None:
p2 = headA
else:
p2 = p2.next
return p1