链表
1. 相交链表
原题链接:160. 相交链表
题解:
class Solution(object):
def getIntersectionNode(self, headA, headB):
p = headA
q = headB
while p is not q:
p = p.next if p else headB
q = q.next if q else headA
return p
2. 反转链表
原题链接:206. 反转链表 1. 方法一:迭代(双指针)
class Solution(object):
def reverseList(self, head):
cur = head
pre = None
while cur:
tmp = cur.next # 暂存后继节点 cur.next
cur.next = pre # 修改 next 引用指向
pre = cur # pre 暂存 cur
cur = tmp # cur 访问下一节点
return pre
2. 方法二:递归
def recur(cur,pre):
if not cur: return pre
res = recur(cur.next,cur)
cur.next = pre
return res
return recur(head,None)
3. 回文链表
原题链接:234. 回文链表
# 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 isPalindrome(self,head):
# 链表的中间结点
def midNode(head):
slow = fast = head
while fast.next and fast.next.next:
fast = fast.next.next
slow = slow.next
return slow
# 反转链表,得到反转后的头结点
def reverseList(head):
def recur(cur,pre):
if not cur:return pre
res = recur(cur.next,cur)
cur.next = pre
return res
return recur(head,None)
# 左链表与右链表一一对比
mid = midNode(head)
right = reverseList(mid.next)
while right:
if head.val != right.val:
return False
head = head.next
right = right.next
return True
4. 环形链表
原题链接:141. 环形链表
class Solution(object):
def hasCycle(self, head):
slow = fast = head
while fast and fast.next: # 同时检查 fast 和 fast.next
slow = slow.next # 慢指针走一步
fast = fast.next.next # 快指针走两步
if slow == fast: # 相遇则说明有环
return True
return False
5. 环形链表 II
原题链接:142. 环形链表 II
class Solution(object):
def detectCycle(self, head):
"""
检测链表中是否存在环,并返回环的入口节点。
若不存在环,返回 None。
使用快慢指针(Floyd判圈算法):
1. 快指针每次走两步,慢指针每次走一步,若存在环,它们必然在环内相遇。
2. 相遇后,将其中一个指针重置到头节点,然后两个指针都每次走一步,
再次相遇的位置即为环的入口。
"""
# 空链表或只有一个节点,不可能有环
if not head or not head.next:
return None
# 初始化快慢指针,都指向头节点
slow = fast = head
# 快指针每次走两步,慢指针走一步,循环条件确保快指针不会越界
# 当 fast 或 fast.next 为 None 时,说明链表无环,循环自然结束
while fast and fast.next:
fast = fast.next.next # 快指针走两步
slow = slow.next # 慢指针走一步
# 如果快慢指针相遇,说明链表中存在环
if fast == slow:
# 将快指针(或慢指针)重置到头节点
fast = head
# 然后两个指针同时以相同速度(每次一步)前进
# 根据数学推导,它们再次相遇的节点就是环的入口
while fast != slow:
fast = fast.next
slow = slow.next
# 返回入口节点
return fast
# 循环正常结束(未触发 return),说明没有环
return None
计算环长:
class Solution(object):
def detectCycle(self, head):
if not head or not head.next:
return None
slow = fast = head
while fast and fast.next:
fast = fast.next.next
slow = slow.next
if fast == slow:
# 有环,记录相遇点
meet = slow
break
else:
return None # 无环
# 计算环的长度:从相遇点出发,走一圈回到原点
length = 1
slow = meet.next
while slow != meet:
slow = slow.next
length += 1
return length # 返回环长
6.合并两个有序链表
原题链接:21. 合并两个有序链表
思路:比较两个链表当前头节点的值,将较小的节点作为合并后的头,然后将其 next 指向剩余部分合并的结果。
class Solution(object):
def mergeTwoLists(self, list1, list2):
if not list1:return list2
if not list2:return list1
if list1.val<=list2.val:
list1.next = self.mergeTwoLists(list1.next,list2)
return list1
else:
list2.next = self.mergeTwoLists(list1,list2.next)
return list2
7.两数相加
原题链接:2. 两数相加
class Solution(object):
def addTwoNumbers(self, l1, l2):
# dummy哨兵节点;cur 是当前尾节点指针(游标)
cur = dummy = ListNode()
carry = 0 # 进位
# 只要任一链表未遍历完,或还有进位,就继续
while l1 or l2 or carry:
s = carry # 当前位数值从进位开始
if l1: # l1 非空,累加并后移
s += l1.val
l1 = l1.next
if l2: # l2 非空,累加并后移
s += l2.val
l2 = l2.next
carry = s // 10 # 更新进位(十位)
cur.next = ListNode(s % 10) # 创建新节点存放当前位(个位)
cur = cur.next # 尾指针后移
return dummy.next # 哨兵的下一个节点即为结果链表的头
8.删除链表的倒数第 N 个结点
原题链接:19. 删除链表的倒数第 N 个结点
class Solution(object):
def removeNthFromEnd(self, head, n):
# 创建哑节点,指向头节点,便于统一处理(包括删除头节点)
dummy = ListNode()
dummy.next = head
fast = slow = dummy # 快慢指针都从哑节点开始
# 快指针先走 n+1 步(让慢指针最终指向被删节点的前驱)
for _ in range(n + 1):
fast = fast.next
# 快慢指针同步移动,直到快指针到达链表末尾
while fast:
fast = fast.next
slow = slow.next
# 此时 slow.next 正是倒数第 n 个节点,跳过它
slow.next = slow.next.next
# 返回真正的头节点(哑节点的下一个)
return dummy.next
9.两两交换链表中的节点
原题链接:24. 两两交换链表中的节点
思路:
class Solution(object):
def swapPairs(self, head):
if not head or not head.next:
return head
node0 = dummy = ListNode(0,head)
node1 = head
while node1 and node1.next:
node2 = node1.next
node3 = node2.next
node0.next = node2
node2.next = node1
node1.next = node3
node0 = node1
node1 = node3
return dummy.next
10.K 个一组翻转链表
原题链接:25. K 个一组翻转链表
class Solution(object):
def reverseKGroup(self, head, k):
# 空链表或只有一个节点,不需要翻转
if not head or not head.next:
return head
# 虚拟头节点,方便处理头节点翻转后的连接
dummy = ListNode(0, head)
# pre 指向待翻转组的前一个节点,end 用来查找当前组的尾节点
pre = end = dummy
# 只要 end 后面还有节点,就继续处理下一组
while end.next:
# 让 end 向后走 k 步,找到当前组的最后一个节点
# 注意:如果剩余节点不足 k 个,end 会变成 None,此时要跳出循环
for _ in range(k):
# 关键:每次移动前检查 end 是否为 None,防止空指针异常
if end is None:
break
end = end.next
# 如果 end 是 None,说明剩余节点不够一组,不翻转,直接结束
if end is None:
break
# 保存下一组的头节点(即当前组尾节点的下一个)
nxt = end.next
# 将当前组从链表中切断,方便单独翻转
end.next = None
# start 是当前组的头节点(翻转前的头)
start = pre.next
# 翻转当前组,翻转后 pre.next 指向新的头(原尾节点)
pre.next = self.reverse(start)
# 翻转后,start 变成了当前组的尾节点,把它接回剩余链表
start.next = nxt
# 移动 pre 和 end 到当前组的尾节点(即 start),准备处理下一组
pre = start
end = start
# 返回虚拟头节点的下一个,即真正的头节点
return dummy.next
def reverse(self, head):
"""
翻转一个单链表,返回新的头节点(原尾节点)
经典三指针法:
- pre:已经翻转好的前一个节点
- cur:当前待翻转的节点
- nxt:保存 cur 的下一个节点,防止丢失
"""
if not head or not head.next:
return head
pre = None
cur = head
# 循环翻转每一个节点
while cur:
nxt = cur.next # 先记住下一个节点
cur.next = pre # 当前节点指向前一个,完成翻转
pre = cur # pre 后移
cur = nxt # cur 后移
return pre
11.随机链表的复制
原题链接:138. 随机链表的复制
class Solution(object):
def copyRandomList(self, head):
if not head:
return None
cur = head
dic = {}
# 第一遍:复制每个节点,存映射
while cur:
dic[cur] = Node(cur.val) # 旧节点 -> 新节点
cur = cur.next
cur = head
# 第二遍:连接 next 和 random(用 get 处理 None)
while cur:
dic[cur].next = dic.get(cur.next) # 查映射连 next
dic[cur].random = dic.get(cur.random) # 查映射连 random
cur = cur.next
return dic[head] # 返回新链表的头
12.排序链表
原题链接:148. 排序链表
class Solution(object):
def sortList(self, head):
# 递归终止条件:空或单节点无需排序
if not head or not head.next:
return head
# 快慢指针找中点,slow 最终停在左半段的尾节点
slow, fast = head, head.next
while fast and fast.next:
fast = fast.next.next
slow = slow.next
# 切断链表,nxt 为右半段头节点
nxt = slow.next
slow.next = None
# 递归排序左右两半
left = self.sortList(head)
right = self.sortList(nxt)
# 合并两个有序链表(哑节点简化操作)
dummy = ListNode()
cur = dummy
while left and right:
if left.val < right.val:
cur.next = left
left = left.next
else:
cur.next = right
right = right.next
cur = cur.next
# 接上剩余部分
cur.next = left if left else right
return dummy.next
13.合并 K 个升序链表
原题链接:23. 合并 K 个升序链表
class Solution(object):
def mergeKLists(self, lists):
# 哨兵节点,简化链表构建
cur = dummy = ListNode()
# 最小堆:存 (节点值, 链表编号, 节点本身)
# 加编号 i 是为了值相等时避免比较 ListNode 对象(会报错)
h = [(head.val, i, head) for i, head in enumerate(lists) if head]
heapify(h) # 将列表转为最小堆
while h:
_, i, node = heappop(h) # 弹出当前最小节点
if node.next: # 该链表还有剩余节点,将下一个入堆
heappush(h, (node.next.val, i, node.next))
cur.next = node # 接到新链表末尾
cur = cur.next # 移动尾指针
return dummy.next # 返回真正的头节点
14.LRU 缓存
原题链接:146. LRU 缓存
思路:
视频:LeetCode刷题|python版本|146题|LRU缓存_哔哩哔哩_bilibili
class ListNode:
# 提高访问属性的速度,并节省内存
__slots__ = 'prev', 'next', 'key', 'value'
def __init__(self, key=0, val=0):
self.key = key
self.val = val
class LRUCache(object):
def __init__(self, capacity):
"""
:type capacity: int
"""
self.capacity = capacity
self.hashmap = {}
self.head = ListNode()
self.tail = ListNode()
self.head.next = self.tail
self.tail.prev = self.head
def remove_node(self,node):
node.prev.next = node.next
node.next.prev = node.prev
def add_node_to_last(self,node):
self.tail.prev.next = node
node.prev = self.tail.prev
node.next = self.tail
self.tail.prev = node
def move_node_to_last(self,node):
self.remove_node(node)
self.add_node_to_last(node)
def get(self, key):
"""
:type key: int
:rtype: int
"""
if key not in self.hashmap:
return -1
node = self.hashmap[key]
self.move_node_to_last(node)
return node.val
def put(self, key, value):
"""
:type key: int
:type value: int
:rtype: None
"""
if key in self.hashmap:
node = self.hashmap[key]
node.val = value
self.move_node_to_last(node)
return
if len(self.hashmap) == self.capacity:
del self.hashmap[self.head.next.key]
self.remove_node(self.head.next)
node = ListNode(key,value)
self.hashmap[key] = node
self.add_node_to_last(node)
# Your LRUCache object will be instantiated and called as such:
# obj = LRUCache(capacity)
# param_1 = obj.get(key)
# obj.put(key,value)