class Solution:
def permute(self, nums: List[int]) -> List[List[int]]:
res = [] # 存放最终所有全排列结果
path = [] # 记录当前递归的路径(临时排列)
n = len(nums)
used = [False] * n # 标记对应下标数字是否已使用
def backtrack():
# 终止条件:路径长度 = 数组长度,说明一组排列完成
if len(path) == n:
res.append(path.copy())
return
# 遍历所有数字,逐个尝试选择
for i in range(n):
if used[i]:
continue # 当前数字已过时,跳过
# 1. 做出选择
used[i] = True
path.append(nums[i])
# 2. 递归深入,继续下一个数
backtrack()
# 3. 回溯撤销选择
path.pop()
used[i] = False
backtrack()
return res
class Trie:
def __init__(self):
# 跟节点,用字典存储子节点,is_end 标记是否是单词结尾
self.root = {}
self.end = '#'
def insert(self, word: str) -> None:
# 从根节点开始
node = self.root
for c in word:
# 字符不存在就新建子字典
if c not in node:
node[c] = {}
# 往下走
node = node[c]
# 标记单词结束
node[self.end] = True
def search(self, word: str) -> bool:
node = self.root
for c in word:
if c not in node:
return False
node = node[c]
# 必须走到单词结尾标记才算存在
return self.end in node
def startsWith(self, prefix: str) -> bool:
node = self.root
for c in prefix:
if c not in node:
return False
node = node[c]
# 只要能走完所有字符,就说明前缀存在
return True
# Your Trie object will be instantiated and called as such:
# obj = Trie()
# obj.insert(word)
# param_2 = obj.search(word)
# param_3 = obj.startsWith(prefix)
class Solution:
def canFinish(self, numCourses: int, prerequisites: List[List[int]]) -> bool:
# 1. 构建有向图:graph[i] 存储课程 i 学完后,可以接着学的所有课程
graph = [[] for _ in range(numCourses)]
# 2. 入度数组:in_degree[i] 表示课程 i 还有几门先修课没修
in_degree = [0] * numCourses
# 3. 遍历先修关系,填充图和入度数组
for a, b in prerequisites:
graph[b].append(a) # 建立边 b->a:学完b才能学a
in_degree[a] += 1 # 课程 a 多了一门先修课,入度 +1
# 4. 初始化队列:把所有【没有先修课、可以直接学】的课程入对
q = deque()
for i in range(numCourses):
if in_degree[i] == 0: # 入度为0表示无先修要求
q.append(i) # 加入队列
count = 0 # 统计已经成功修完的课程数量
# 5. BFS 核心循环:不断处理队列中可以学的课
while q: # 只要队列不为空,就还有课能学
u = q.popleft() # 从队列头部取出一门课 u,开始学习
count += 1 # 学完一门课,计数 +1
# 6. 遍历 u 的所有后续课程 v (v学完u才能学的课)
for v in graph[u]:
in_degree[v] -= 1
# 7. 如果 v 的入度变为0,说明 v 所有先修课都完成了
if in_degree[v] == 0:
q.append(v) # 把v加入队列,可以开始学了
# 8. 最终判断:修完的课程数 == 总课程数 -> 无环,可以修完
return count == numCourses
# 定义双向链表节点类
# 每个节点需要存储:key、value、前驱指针、后继指针
class DLinkedNode:
def __init__(self, key=0, value=0):
self.key = key # 必须存key!删除链表尾节点时,要靠它去哈希表删数据
self.value = value # 缓存存储的值
self.prev = None # 前驱节点指针
self.next = None # 后继节点指针
# LRU 缓存实现类
# 核心:哈希表 O(1) 查找 + 双向链表 O(1) 增删
class LRUCache:
# 初始化构造方法
def __init__(self, capacity: int):
self.capacity = capacity # 缓存最大容量(上限)
self.size = 0 # 当前缓存里实际存了多少数据
self.cache = {} # 哈希表:key -> 双向链表节点,实现 O(1) 查找
# 创建【虚拟头节点】和【虚拟尾节点】
# 作用:不用判断空链表、头插、尾删的边界情况
self.head = DLinkedNode()
self.tail = DLinkedNode()
# 初始化双向链表:head <-> tail
self.head.next = self.tail
self.tail.prev = self.head
# 查询操作:根据 key 获取 value
def get(self, key: int) -> int:
# 如果 key 不在缓存里,返回 -1
if key not in self.cache:
return -1
# key 存在:通过哈希表找到对应的节点
node = self.cache[key]
# 访问过了,把节点移到链表头部(标记为【最近使用】)
self.moveToHead(node)
# 返回节点的值
return node.value
# 插入/更新操作
def put(self, key: int, value: int) -> None:
if key in self.cache:
# 情况1:key 已存在
# 找到节点,更新 value
node = self.cache[key]
node.value = value
# 访问过,移到头部
self.moveToHead(node)
else:
# 情况2:key 不存在,新建节点
new_node = DLinkedNode(key, value)
# 加入哈希表
self.cache[key] = new_node
# 缓存数量 +1
self.size += 1
# 如果缓存满了,需要删除【最久未使用】的节点(链表尾部)
if self.size > self.capacity:
# 删除尾部节点
tail_node = self.removeTail()
# 同时在哈希表里删除这个 key
del self.cache[tail_node.key]
# 缓存数量 -1
self.size -= 1
# 把新节点加到链表头部
self.addToHead(new_node)
# 工具方法:把一个节点【添加到链表头部】
def addToHead(self, node: DLinkedNode):
# 新节点的前驱指向 head
node.prev = self.head
# 新节点的后继指向 head 原来的 next
node.next = self.head.next
# 原来的头节点的前驱,指向新节点
self.head.next.prev = node
# head 的 next 指向新节点
self.head.next = node
# 工具方法:从双向链表中【删除指定节点】
def removeNode(self, node: DLinkedNode):
# 拿到要删除节点的 前驱 和 后继
prev_node = node.prev
next_node = node.next
# 直接跳过当前节点,让前后节点互相连接
prev_node.next = next_node
next_node.prev = prev_node
# 工具方法:把节点【移动到头部】
# 两步:先删除,再插入头部
def moveToHead(self, node: DLinkedNode):
self.removeNode(node)
self.addToHead(node)
# 工具方法:【删除尾部节点】(最久未使用)
def removeTail(self) -> DLinkedNode:
# 尾节点的前驱,就是真正的最后一个有效节点
tail_node = self.tail.prev
# 从链表中删掉它
self.removeNode(tail_node)
# 返回被删掉的节点(用于去哈希表删 key)
return tail_node
# Your LRUCache object will be instantiated and called as such:
# obj = LRUCache(capacity)
# param_1 = obj.get(key)
# obj.put(key,value)
# Definition for singly-linked list.
# class ListNode:
# def __init__(self, val=0, next=None):
# self.val = val
# self.next = next
class Solution:
# 合并两个链表
def merge(self, h1: Optional[ListNode], h2: Optional[ListNode]) -> Optional[ListNode]:
cur = dummy = ListNode(0)
while h1 and h2:
if h1.val < h2.val:
cur.next = h1
h1 = h1.next
else:
cur.next = h2
h2 = h2.next
cur = cur.next
cur.next = h1 if h1 else h2
return dummy.next
def sortList(self, head: Optional[ListNode]) -> Optional[ListNode]:
if not head or not head.next:
return head
# 获取链表总长度
n = 0
cur = head
while cur:
n += 1
cur = cur.next
dummy = ListNode(0, head)
intv = 1
while intv < n:
pre, cur = dummy ,dummy.next
while cur:
# 截取第一段有序链表
h1 = cur
for _ in range(intv - 1):
if cur and cur.next:
cur = cur.next
else:
break
# 获取第二段有序链表,并断开第一段链表
h2 = cur.next
cur.next = None
cur = h2
for _ in range(intv - 1):
if cur and cur.next:
cur = cur.next
else:
break
# 获取下一轮开始的指针,并断开第二段链表
next_node = None
if cur:
next_node = cur.next
cur.next = None
# 合并两个链表
merged = self.merge(h1, h2)
pre.next = merged
while pre.next:
pre = pre.next
cur = next_node
intv *= 2
return dummy.next
class Solution:
def copyRandomList(self, head: 'Optional[Node]') -> 'Optional[Node]':
# 边界情况:如果原链表为空,直接返回空
if not head:
return None
# 创建哈希表,用于存储 原节点 -> 新复制节点 的映射关系
node_map = {}
# 定义游标指针,从头节点开始遍历
cur = head
# 第一次遍历:创建所有新节点,只赋值val,建立原节点和新节点的映射
while cur:
# 根据当前原节点的值,创建一个新的复制节点
new_node = Node(cur.val)
# 把 原节点:新节点 存入字典,方便后续查找对应关系
node_map[cur] = new_node
# 游标向后移动,继续处理下一个原节点
cur = cur.next
# 第二次遍历:给新节点设置 next 和 random 指针
cur = head # 重置游标,回到链表头
while cur:
# 从字典中取出当前原节点对应的新节点
new_node = node_map[cur]
# 新节点的next = 原节点next对应的新节点(没有则为None)
new_node.next = node_map.get(cur.next, None)
# 新节点的random = 原节点random对应的新节点(没有则为None)
new_node.random = node_map.get(cur.random, None)
# 游标向后移动
cur = cur.next
# 返回复制链表的头节点(原头节点对应的新节点)
return node_map[head]
class Solution:
def copyRandomList(self, head: 'Optional[Node]') -> 'Optional[Node]':
# 边界情况:链表为空直接返回
if not head:
return None
# 第一步:在每个原节点后面插入对应的复制节点
cur = head
while cur:
# 创建当前节点的复制节点
copy = Node(cur.val)
# 复制节点的next指向原节点的next
copy.next = cur.next
# 原节点的next指向复制节点,完成插入
cur.next = copy
# 游标移动到下一个原节点(跳过刚插入的复制节点)
cur = copy.next
# 第二步:给所有复制节点设置 random 指针
cur = head # 重置游标
while cur:
# 如果原节点有random指向,那么复制节点的random = 原节点random的下一个节点(对应复制节点)
if cur.random:
cur.next.random = cur.random.next
# 每次跳两步,只遍历原节点
cur = cur.next.next
# 第三步:将原链表和复制链表分离,得到最终的深拷贝链表
cur = head
copy_head = head.next # 记录复制链表的头节点,最后要返回
copy_cur = copy_head # 复制链表的游标
while cur:
# 原节点的next恢复为原next(跳过复制节点)
cur.next = copy_cur.next
# 原链表游标后移
cur = cur.next
# 如果还有后续节点,复制链表游标继续后移
if cur:
copy_cur.next = cur.next
copy_cur = copy_cur.next
# 返回复制好的新链表头
return copy_head