链表的类型:单链表、双链表、循环链表
链表节点的定义:
class ListNode:
def __init__(self, val, next=None):
self.val = val
self.next = next
链表操作中一个重要的技巧是虚拟头节点:链表操作当前节点必须要找前一个节点才能操作,这就造成了头结点的尴尬,因为头结点没有前一个节点了,每次头结点都要单独处理,使用虚拟头结点可以简化代码
双指针的应用
LeetCode142题 环形链表II
快慢指针 + 数学推导
快慢指针如果相遇,说明有环;此时让慢指针回到头节点,再同速度移动两个指针,相遇节点即为环的入口
2 * (t + k1 * c + c0) = t + k2 * c + c0 -> t = (k2 - 2 * k1) * c - c0
# Definition for singly-linked list.
# class ListNode:
# def __init__(self, x):
# self.val = x
# self.next = None
class Solution:
def detectCycle(self, head: Optional[ListNode]) -> Optional[ListNode]:
fast = head
slow = head
while fast and fast.next:
slow = slow.next
fast = fast.next.next
if fast == slow:
break
if fast is None or fast.next is None:
return None
slow = head
while slow != fast:
slow = slow.next
fast = fast.next
return fast
LeetCode160题 相交链表
双指针移动,使得两个链表尾部对齐,此时同时移动两个指针,相等的节点就是相交的节点
# Definition for singly-linked list.
# class ListNode:
# def __init__(self, x):
# self.val = x
# self.next = None
class Solution:
def getIntersectionNode(self, headA: ListNode, headB: ListNode) -> Optional[ListNode]:
cur_a, cur_b = headA, headB
while cur_a and cur_b:
cur_a = cur_a.next
cur_b = cur_b.next
if cur_a:
cur = cur_a
cur_l, cur_s = headA, headB
else:
cur = cur_b
cur_l, cur_s = headB, headA
while cur:
cur = cur.next
cur_l = cur_l.next
while cur_l and cur_s:
if cur_l == cur_s:
return cur_l
cur_l = cur_l.next
cur_s = cur_s.next
return None
LeetCode19题 删除链表的倒数第N个节点
设置虚拟头结点,以方便处理删除实际头结点的逻辑
使用快慢指针,先让快指针移动到第N个节点上,然后快慢指针同时移动,当快指针移动到末尾节点时,慢指针的下一个节点就是要删除的倒数第N个节点
# Definition for singly-linked list.
# class ListNode(object):
# def __init__(self, val=0, next=None):
# self.val = val
# self.next = next
class Solution(object):
def removeNthFromEnd(self, head, n):
"""
:type head: ListNode
:type n: int
:rtype: ListNode
"""
dummy_head = ListNode(next=head)
slow = dummy_head
fast = dummy_head
for i in range(n):
fast = fast.next
while fast.next:
slow = slow.next
fast = fast.next
slow.next = slow.next.next
return dummy_head.next
LeetCode86题 分隔链表
把原链表分成两个小链表,一个链表中的元素大小都小于x
,另一个链表中的元素都大于等于x
,最后再把这两条链表接到一起,就得到了想要的结果,需要注意每一步要断开原链表中的每个节点的next指针,否则会超出内存限制
# Definition for singly-linked list.
# class ListNode:
# def __init__(self, val=0, next=None):
# self.val = val
# self.next = next
class Solution:
def partition(self, head: Optional[ListNode], x: int) -> Optional[ListNode]:
small_dummy = ListNode()
big_dummy = ListNode()
small_cur, big_cur = small_dummy, big_dummy
while head:
if head.val >= x:
big_cur.next = head
big_cur = big_cur.next
head = head.next
# 断开原链表中的每个节点的next指针
big_cur.next = None
else:
small_cur.next = head
small_cur = small_cur.next
head = head.next
# 断开原链表中的每个节点的next指针
small_cur.next = None
small_cur.next = big_dummy.next
return small_dummy.next
反转链表
LeetCode206题 反转链表
迭代法
# Definition for singly-linked list.
# class ListNode:
# def __init__(self, val=0, next=None):
# self.val = val
# self.next = next
class Solution:
def reverseList(self, head: Optional[ListNode]) -> Optional[ListNode]:
dummy = ListNode()
while head:
tmp = dummy.next
dummy.next = head
head = head.next
dummy.next.next = tmp
return dummy.next
递归法
# Definition for singly-linked list.
# class ListNode:
# def __init__(self, val=0, next=None):
# self.val = val
# self.next = next
class Solution:
def reverseList(self, head: Optional[ListNode]) -> Optional[ListNode]:
if not head or not head.next:
return head
last = self.reverseList(head.next)
head.next.next = head
head.next = None
return last
LeetCode92题 反转链表II
# Definition for singly-linked list.
# class ListNode:
# def __init__(self, val=0, next=None):
# self.val = val
# self.next = next
class Solution:
def reverseBetween(self, head: Optional[ListNode], left: int, right: int) -> Optional[ListNode]:
if left == 1:
return self.reverseN(head, right)
head.next = self.reverseBetween(head.next, left - 1, right - 1)
return head
def reverseN(self, head, n):
# 反转前n个节点的函数
global n_1
if n == 1:
n_1 = head.next
return head
last = self.reverseN(head.next, n - 1)
head.next.next = head
head.next = n_1
return last
LeetCode25题 K个一组翻转链表
遍历链表,记录节点数量,对满足k长度的一段链表进行反转,然后继续遍历直至结束
# Definition for singly-linked list.
# class ListNode:
# def __init__(self, val=0, next=None):
# self.val = val
# self.next = next
class Solution:
def reverseKGroup(self, head: Optional[ListNode], k: int) -> Optional[ListNode]:
dummy = ListNode()
count = 0
fast = head
slow = head
cur = dummy
while fast:
count += 1
if count % k == 0:
tmp = fast.next
fast.next = None
cur.next = self.reverse(slow)
cur = slow
fast = tmp
slow = tmp
else:
fast = fast.next
if cur:
cur.next = slow
return dummy.next
def reverse(self, head):
if not head or not head.next:
return head
last = self.reverse(head.next)
head.next.next = head
head.next = None
return last
LeetCode234题 回文链表
用快慢指针遍历到中间节点,对后半部分进行反转,然后顺序比较链表值
# Definition for singly-linked list.
# class ListNode:
# def __init__(self, val=0, next=None):
# self.val = val
# self.next = next
class Solution:
def isPalindrome(self, head: Optional[ListNode]) -> bool:
fast = head
slow = head
while fast and fast.next and fast.next.next:
fast = fast.next.next
slow = slow.next
reverse = self.reverseList(slow.next)
while head and reverse:
if head.val != reverse.val:
return False
head = head.next
reverse = reverse.next
return True
def reverseList(self, head):
if not head or not head.next:
return head
last = self.reverseList(head.next)
head.next.next = head
head.next = None
return last
链表与优先队列的结合
LeetCode23题 合并K个升序链表
优先队列应用:把链表节点放入一个最小堆,就可以每次获得k
个节点中的最小节点,算法整体的时间复杂度是O(nlogk),其中k
是链表的条数,n是这些链表的节点总数
# Definition for singly-linked list.
# class ListNode:
# def __init__(self, val=0, next=None):
# self.val = val
# self.next = next
class Solution:
def mergeKLists(self, lists: List[Optional[ListNode]]) -> Optional[ListNode]:
import heapq
dummy = ListNode()
cur = dummy
head = []
for i in range(len(lists)):
if lists[i]:
heapq.heappush(head, (lists[i].val, i))
lists[i] = lists[i].next
while head:
val, idx = heapq.heappop(head)
cur.next = ListNode(val)
cur = cur.next
if lists[idx]:
heapq.heappush(head, (lists[idx].val, idx))
lists[idx] = lists[idx].next
return dummy.next