leetcode刷题记录
这个文档仅用于记录leetcode热题100的记录,重点记录我这个菜鸡认为惊为天人的解题思路。
有关python的collections库
deque
deque(双端队列)是一个线程安全的双向队列,支持在两端快速添加和删除元素。
1 | from collections import deque |
Counter
Counter是一个字典的子类,用于计数可哈希对象
1 | from collections import Counter |
defaultdict
defaultdict是dict的一个子类,提供了一个默认值,当访问不存在的键时不会抛出KeyError
1 | from collections import defaultdict |
哈希
【哈希】两数之和
题面
给定一个整数数组 nums 和一个整数目标值 target,请你在该数组中找出 和为目标值 target 的那 两个 整数,并返回它们的数组下标。
你可以假设每种输入只会对应一个答案,并且你不能使用两次相同的元素。
你可以按任意顺序返回答案。
solution:暴力题解
1 | class Solution: |
【哈希】异位字符
题面
给你一个字符串数组,请你将 字母异位词 组合在一起。可以按任意顺序返回结果列表。
字母异位词 是由重新排列源单词的所有字母得到的一个新单词。
Solution:defultdict
将排序后的字符当作键,遍历所有字符串,符合要求的存为值。
1 | class Solution: |
【哈希】最长连续序列
题面
给定一个未排序的整数数组 nums ,找出数字连续的最长序列(不要求序列元素在原数组中连续)的长度。
请你设计并实现时间复杂度为 O(n) 的算法解决此问题。
Solution 遍历
1 | def longestConsecutive(self, nums: List[int]) -> int: |
动态规划
【动态规划】爬楼梯
题面
假设你正在爬楼梯。需要 n 阶你才能到达楼顶。
每次你可以爬 1 或 2 个台阶。你有多少种不同的方法可以爬到楼顶呢?
Solution
动态规划自底向上
1 | class Solution: |
【动态规划】杨辉三角
题面
给定一个非负整数 numRows,生成「杨辉三角」的前 numRows 行。
在「杨辉三角」中,每个数是它左上方和右上方的数的和。
Solution
1 | class Solution: |
双指针
【双指针】移动0
题面
给定一个数组 nums,编写一个函数将所有 0 移动到数组的末尾,同时保持非零元素的相对顺序。请注意 ,必须在不复制数组的情况下原地对数组进行操作。
Solution:
双指针,i用来遍历所有元素,j寻找放置非0的位置,然后再一次循环把剩下的位置置为0
1 | class Solution: |
【双指针】乘最多水的容器
题面
给定一个长度为 n 的整数数组 height 。有 n 条垂线,第 i 条线的两个端点是 (i, 0) 和 (i, height[i]) 。
找出其中的两条线,使得它们与 x 轴共同构成的容器可以容纳最多的水。
返回容器可以储存的最大水量。
说明:你不能倾斜容器。
Solution
设置双指针,左右各一。
1 | class Solution: |
【双指针】三数之和
题面:
给你一个整数数组 nums ,判断是否存在三元组 [nums[i], nums[j], nums[k]] 满足 i != j、i != k 且 j != k ,同时还满足 nums[i] + nums[j] + nums[k] == 0 。请你返回所有和为 0 且不重复的三元组。
注意:答案中不可以包含重复的三元组。
Solution:
双指针之前排序,固定第一个数nums[i]为结果中的第一个数(这个数的结果等于另外两个数求和取负),然后在剩下的元素中设置双指针挨个寻找符合要求的数,相同则跳过。
1 | class Solution: |
【双指针】接雨水
题面

给定 n 个非负整数表示每个宽度为 1 的柱子的高度图,计算按此排列的柱子,下雨之后能接多少雨水
Solution:
双指针分别从左右两边开始遍历,谁小处理谁。如果左边小,比较左边当前位置的高度和最高位置谁大,要是当前位置大于最大位置,则更新最大位置,否则计算雨水(最高-当前),然后左边指针右移;右边同理。
1 | class Solution: |
滑动窗口
【滑动窗口】无重复字符的最长字串
题面:
给定一个字符串 s ,请你找出其中不含有重复字符的 最长 子串 的长度。
Solution:
初始化:
seen集合存储当前窗口字符,left指针初始化为 0,max_length记录最长子串长度。遍历字符串:
字符存在时:移动
left并从seen中移除字符,直到s[right]不在集合中。字符不存在时:添加到
seen,并更新max_length。
返回结果:
max_length即为最长无重复字符子串的长度。
1 | class Solution: |
【滑动窗口】找到字符串中所有字母异位词
题面
给定两个字符串 s 和 p,找到 s 中所有 p 的 异位词 的子串,返回这些子串的起始索引。不考虑答案输出的顺序。
Solution
1 | class Solution: |
数组
【普通数组】最大子数和
题目
给你一个整数数组 nums ,请你找出一个具有最大和的连续子数组(子数组最少包含一个元素),返回其最大和。
子数组是数组中的一个连续部分。
Solution
Kadane算法(动态规划)
初始化:
max_current:记录以当前元素结尾的最大子数组和。max_global:记录全局最大子数组和。
遍历数组:
对于每个元素,更新
max_current:要么将当前元素加入之前的子数组(
max_current + nums[i]),要么以当前元素作为新的子数组起点(
nums[i])。
更新
max_global为max_current和max_global中的较大值。
返回结果:
max_global即为最大子数组和。
1 | class Solution: |
【普通数组】合并区间
题目
以数组 intervals 表示若干个区间的集合,其中单个区间为 intervals[i] = [starti, endi] 。请你合并所有重叠的区间,并返回 一个不重叠的区间数组,该数组需恰好覆盖输入中的所有区间 。
Solution
排序区间:首先按照区间的起始点进行排序,这样相邻的区间更容易判断是否重叠。
合并区间:遍历排序后的区间列表,逐个检查当前区间是否与结果列表中的最后一个区间重叠:
如果重叠,则合并这两个区间(更新结果列表最后一个区间的结束点为两者中的较大值)。
如果不重叠,则将当前区间直接加入结果列表。
1 | class Solution: |
【普通数组】轮转数组
题目
给定一个整数数组 nums,将数组中的元素向右轮转 k 个位置,其中 k 是非负数。
Solution
1 | class Solution:#python切片 |
创建一个新数组,将原数组的元素按照轮转后的位置放入新数组。
计算每个元素的新位置:
(i + k) % n,其中n是数组长度。1
2
3
4
5
6
7class Solution:
def rotate(self, nums: List[int], k: int) -> None:
n = len(nums)
rotated = [0] * n
for i in range(n):
rotated[(i + k) % n] = nums[i]
nums[:] = rotated
【普通数组】除自身以外的数组乘积
题目
给你一个整数数组 nums,返回 数组 answer ,其中 answer[i] 等于 nums 中除 nums[i] 之外其余各元素的乘积 。
题目数据 保证 数组 nums之中任意元素的全部前缀元素和后缀的乘积都在 32 位 整数范围内。
请 不要使用除法,且在 O(n) 时间复杂度内完成此题。
Solution
维护两个数组,对于numi,lefti和righti分别表示左右两边的乘积
1 | class Solution: |
【普通数组】最小数
题目
给你一个未排序的整数数组 nums ,请你找出其中没有出现的最小的正整数。
请你实现时间复杂度为 O(n) 并且只使用常数级别额外空间的解决方案。
Solution
来自ds的解法
class Solution:
def firstMissingPositive(self, nums: List[int]) -> int:
n = len(nums)
# 检查是否包含1
if 1 not in nums:
return 1
# 预处理:将所有非正数和大于n的数替换为1
for i in range(n):
if nums[i] <= 0 or nums[i] > n:
nums[i] = 1
# 使用索引作为哈希键,标记出现的数字
for i in range(n):
num = abs(nums[i])
if num == n:
nums[0] = -abs(nums[0]) # 用索引0表示n
else:
nums[num] = -abs(nums[num])
# 查找第一个正数的索引
for i in range(1, n):
if nums[i] > 0:
return i
# 检查n是否出现
if nums[0] > 0:
return n
# 如果1到n都出现,则返回n+1
return n + 1
矩阵
【矩阵】矩阵置零
题面
给定一个 *m* x *n* 的矩阵,如果一个元素为 0 ,则将其所在行和列的所有元素都设为 0 。请使用 原地 算法
Solution:
1 | class Solution: |
【矩阵】螺旋矩阵
题面:
给你一个 m 行 n 列的矩阵 matrix ,请按照 顺时针螺旋顺序 ,返回矩阵中的所有元素。

Solution:
【边界模拟】
初始化边界:
上边界
top = 0,下边界bottom = m - 1左边界
left = 0,右边界right = n - 1
循环遍历:
从左到右遍历上边界,完成后上边界下移(
top += 1)从上到下遍历右边界,完成后右边界左移(
right -= 1)检查是否仍需遍历(
top <= bottom且left <= right):从右到左遍历下边界,完成后下边界上移(
bottom -= 1)从下到上遍历左边界,完成后左边界右移(
left += 1)
终止条件:当所有边界相遇或交叉时停止。
1 | class Solution: |
【矩阵】旋转图像
题面
给定一个 n × n 的二维矩阵 matrix 表示一个图像。请你将图像顺时针旋转 90 度。
你必须在 原地 旋转图像,这意味着你需要直接修改输入的二维矩阵。请不要 使用另一个矩阵来旋转图像。

Solution
转置矩阵:首先将矩阵的行和列互换,即转置矩阵。转置后的矩阵会将原来的行变为列,原来的列变为行。
翻转每一行:然后将转置后的矩阵的每一行进行翻转,即第一个元素和最后一个元素交换,第二个元素和倒数第二个元素交换,以此类推。
这样操作后,矩阵就会顺时针旋转 90 度。
1 | class Solution: |
【矩阵】搜索二维矩阵Ⅱ
题面
编写一个高效的算法来搜索 *m* x *n* 矩阵 matrix 中的一个目标值 target 。该矩阵具有以下特性:
- 每行的元素从左到右升序排列。
- 每列的元素从上到下升序排列。
Solution
从矩阵的左下角或者右上角开始遍历,因为这两个位置分别对应所在行的最大(小)值和所在列的最小(大)值。示例从右上角开始,如果当前值大于目标,则行号+1,小于目标则列号减一。
1 | class Solution: |
【矩阵】和为K的子数组
题面
给你一个整数数组 nums 和一个整数 k ,请你统计并返回 该数组中和为 k 的子数组的个数 。
子数组是数组中元素的连续非空序列。
Solution:
一开始用滑动窗口结果超时了,换个思路,前缀和+哈希表:
前缀和:定义
prefix_sum[i]为nums[0] + nums[1] + ... + nums[i-1]。关键观察:子数组
nums[i..j]的和可以表示为prefix_sum[j+1] - prefix_sum[i]。哈希表优化:
用哈希表记录前缀和出现的次数。
对于当前前缀和
curr_sum,检查curr_sum - k是否在哈希表中,若存在则累加计数。
1 | from collections import defaultdict |
链表
【链表】反转链表
题目
给你单链表的头节点 head ,请你反转链表,并返回反转后的链表。
Solution
1 | class Solution: |
【链表】交叉链表
题目
给你两个单链表的头节点 headA 和 headB ,请你找出并返回两个单链表相交的起始节点。如果两个链表不存在相交节点,返回 null 。
Solution
1 | class Solution: |
思路
设两个指针 pA 和 pB,分别从 headA 和 headB 出发。
每次向前走一步,如果到达链表末尾,则切换到另一个链表的头。
如果两个链表相交,最终会在相交点相遇;
如果不相交,则会同时走到 None,返回 None。
为什么有效?
指针 A 走过的路径:lenA + lenB
指针 B 走过的路径:lenB + lenA
若相交,则在交点对齐;若不相交,则同时为 None。
【链表】回文链表
题目
给你一个单链表的头节点 head ,请你判断该链表是否为回文链表。如果是,返回 true ;否则,返回 false 。
输入:head = [1,2,2,1]
输出:true
输入:head = [1,2]
输出:false
Solution
- 使用两个指针(快慢指针)寻找链表中点,慢指针一次走一格,快指针一次走两格,这样快指针走完时,慢指针就指向中点。
- 反转从慢指针到快指针之间的链表。
- 对比反转后的链表和整个链表的前半部分是否相同。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38# Definition for singly-linked list.
# class ListNode:
# def __init__(self, val=0, next=None):
# self.val = val
# self.next = next
from typing import Optional
class Solution:
def isPalindrome(self, head: Optional[ListNode]) -> bool:
if not head or not head.next:
return True
# 1. 快慢指针找中点
slow, fast = head, head
while fast and fast.next:
slow = slow.next
fast = fast.next.next
# 2. 反转后半部分
prev = None
curr = slow
while curr:
nxt = curr.next
curr.next = prev
prev = curr
curr = nxt
# 3. 对比两半部分
p1, p2 = head, prev
while p2: # 后半部分长度 <= 前半部分
if p1.val != p2.val:
return False
p1 = p1.next
p2 = p2.next
return True
【链表】环形链表
题面
给你一个链表的头节点 head ,判断链表中是否有环。
如果链表中有某个节点,可以通过连续跟踪 next 指针再次到达,则链表中存在环。 为了表示给定链表中的环,评测系统内部使用整数 pos 来表示链表尾连接到链表中的位置(索引从 0 开始)。注意:pos 不作为参数进行传递 。仅仅是为了标识链表的实际情况。
如果链表中存在环 ,则返回 true 。 否则,返回 false 。
Solution
设置两个指针:
slow 每次走一步
fast 每次走两步
如果链表里有环:
fast 和 slow 一定会在环里相遇(类似跑道上快慢选手总能相遇)。
如果链表无环:
fast 或 fast.next 会先到达 None,说明走到链表末尾。
1 | class Solution: |
【链表】环形链表Ⅱ
题面
给定一个链表的头节点 head ,返回链表开始入环的第一个节点。 如果链表无环,则返回 null。
如果链表中有某个节点,可以通过连续跟踪 next 指针再次到达,则链表中存在环。 为了表示给定链表中的环,评测系统内部使用整数 pos 来表示链表尾连接到链表中的位置(索引从 0 开始)。如果 pos 是 -1,则在该链表中没有环。注意:pos 不作为参数进行传递,仅仅是为了标识链表的实际情况。
不允许修改 链表。
Solution
先寻找之前题目中slow和fast相遇的位置,然后将slow移到开始,再让两个指针同速往前遍历,再次相遇时就是环的入口。
1 | class Solution: |
【链表】合并两个有序链表
题面
将两个升序链表合并为一个新的 升序 链表并返回。新链表是通过拼接给定的两个链表的所有节点组成的。
Solution
1 | class Solution: |
二叉树
【二叉树】中序遍历
题面
中序遍历二叉树
Solution
递归
1 | class Solution: |
迭代
1 | def inorderTraversal(self, root: Optional[TreeNode]) -> List[int]: |
【二叉树】最大深度
题面
给定一个二叉树 root ,返回其最大深度。
二叉树的 最大深度 是指从根节点到最远叶子节点的最长路径上的节点数。
Solution
递归
递归寻找最大深度
1 | # Definition for a binary tree node. |
迭代
借助队列,首先将根节点入队。当队列非空时,进行以下步骤:
- 队尾元素弹出,记录该层节点数量,弹出队尾;
- 循环所有该层节点,判断是否有左右子树
- 若有子树,将该节点入队,深度+1
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19class Solution:
def maxDepth(self, root: Optional[TreeNode]) -> int:
if not root:
return 0
queue = deque([root])
depth = 0
while queue:
level_size = len(queue)
for _ in range(level_size):
node = queue.popleft()
if node.left:
queue.append(node.left)
if node.right:
queue.append(node.right)
depth += 1
return depth
【二叉树】翻转
题面
给你一棵二叉树的根节点 root ,翻转这棵二叉树,并返回其根节点。
Solution
递归
1 | # Definition for a binary tree node. |
迭代
1 | class Solution: |


