哈希

  • 两数之和

给定一个整数数组 nums 和一个整数目标值 target,请你在该数组中找出 和为目标值 target 的那 两个 整数,并返回它们的数组下标。

你可以假设每种输入只会对应一个答案,并且你不能使用两次相同的元素。

你可以按任意顺序返回答案。

1
2
3
4
5
6
7
class Solution:
def twoSum(self, nums: List[int], target: int) -> List[int]:
hashtable = {}
for index, num in enumerate(nums):
if (target-num) in hashtable:
return [index, hashtable[target-num]]
hashtable[num] = index
  • 字母异位词分组

给你一个字符串数组,请你将 字母异位词 组合在一起。可以按任意顺序返回结果列表。

字母异位词 是由重新排列源单词的所有字母得到的一个新单词。

1
2
3
4
5
6
7
8
9
10
class Solution:
def groupAnagrams(self, strs: List[str]) -> List[List[str]]:
ans = {}
for s in strs:
key = ''.join(sorted(s))
if key not in ans:
ans[key] = [s]
else:
ans[key].append(s)
return list(ans.values())
  • 最长连续序列

给定一个未排序的整数数组 nums ,找出数字连续的最长序列(不要求序列元素在原数组中连续)的长度。

请你设计并实现时间复杂度为 O(n) 的算法解决此问题。

1
2
3
4
5
6
7
8
9
10
11
12
class Solution:
def longestConsecutive(self, nums: List[int]) -> int:
ans = 0
st = set(nums)
for x in st:
if x-1 in st:
continue
y = x+1
while y in st:
y = y + 1
ans = max(ans,y -x)
return ans

双指针

  • 移动0
1
2
3
4
5
6
7
8
9
10
11
class Solution:
def moveZeroes(self, nums: List[int]) -> None:
"""
Do not return anything, modify nums in-place instead.
"""
left = right = 0
while right < len(nums):
if nums[right]!=0:
nums[left],nums[right]=nums[right],nums[left]
left += 1
right += 1
  • 盛水最多容器

给定一个长度为 n 的整数数组 height 。有 n 条垂线,第 i 条线的两个端点是 (i, 0)(i, height[i])

找出其中的两条线,使得它们与 x 轴共同构成的容器可以容纳最多的水。

返回容器可以储存的最大水量。

**说明:**你不能倾斜容器。

1
2
3
4
5
6
7
8
9
10
11
12
13
class Solution:
def maxArea(self, height: List[int]) -> int:
left = ans = 0
right = len(height) - 1
while left < right :
if height[left] < height[right]:
ans = max(ans,height[left]*(right-left))
left += 1
else:
ans = max(ans,height[right]*(right-left))
right -= 1
return ans

  • 三数之和

给你一个整数数组 nums ,判断是否存在三元组 [nums[i], nums[j], nums[k]] 满足 i != ji != kj != k ,同时还满足 nums[i] + nums[j] + nums[k] == 0 。请你返回所有和为 0 且不重复的三元组。

**注意:**答案中不可以包含重复的三元组。

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
class Solution:
def threeSum(self, nums: List[int]) -> List[List[int]]:
nums.sort()
ans = []
n = len(nums)
for i in range(n - 2):
x = nums[i]
if i > 0 and x == nums[i - 1]: # 跳过重复数字
continue
if x + nums[i + 1] + nums[i + 2] > 0: # 优化一
break
if x + nums[-2] + nums[-1] < 0: # 优化二
continue
j = i + 1
k = n - 1
while j < k:
s = x + nums[j] + nums[k]
if s > 0:
k -= 1
elif s < 0:
j += 1
else: # 三数之和为 0
ans.append([x, nums[j], nums[k]])
j += 1
while j < k and nums[j] == nums[j - 1]: # 跳过重复数字
j += 1
k -= 1
while k > j and nums[k] == nums[k + 1]: # 跳过重复数字
k -= 1
return ans
  • 接雨水

    给定 n 个非负整数表示每个宽度为 1 的柱子的高度图,计算按此排列的柱子,下雨之后能接多少雨水。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    class Solution:
    def trap(self, height: List[int]) -> int:
    if not height:
    return 0

    left, right = 0, len(height) - 1 # 两个指针
    left_max, right_max = height[left], height[right] # 左右最大边界
    ans = 0 # 结果累加器

    while left < right:
    if height[left] < height[right]:
    left += 1
    if height[left] > left_max:
    left_max = height[left]
    else:
    ans += left_max - height[left]
    else:
    right -= 1
    if height[right] > right_max:
    right_max = height[right]
    else:
    ans += right_max - height[right]

    return ans

    滑动窗口

  • 无重复字符的最长字串

    给定一个字符串 s ,请你找出其中不含有重复字符的 最长 子串 的长度。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    class Solution:
    def lengthOfLongestSubstring(self, s: str) -> int:
    d = {}
    res = 0
    start = -1
    for index, c in enumerate(s):
    if c not in d:
    res = max(res, index-start)
    else:
    if d[c] > start:
    start = d[c]
    else:
    res = max(res, index-start)
    d[c] = index
    return res
  • 找到字符串中所有字母异位词

    给定两个字符串 sp,找到 s 中所有 p异位词 的子串,返回这些子串的起始索引。不考虑答案输出的顺序。

    示例 1:

    1
    2
    3
    4
    5
    输入: s = "cbaebabacd", p = "abc"
    输出: [0,6]
    解释:
    起始索引等于 0 的子串是 "cba", 它是 "abc" 的异位词。
    起始索引等于 6 的子串是 "bac", 它是 "abc" 的异位词。
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
39
40
41
42
43
44
class Solution:
def findAnagrams(self, s: str, p: str) -> List[int]:
# 获取字符串 s 和 p 的长度
s_len, p_len = len(s), len(p)

# 如果 s 的长度小于 p 的长度,直接返回空列表,因为无法形成异位词子串
if s_len < p_len:
return []

# 初始化结果列表,用于存储异位词子串的起始索引
ans = []

# 初始化两个长度为 26 的数组,用于统计字符频率
# s_count 用于统计当前窗口内的字符频率
# p_count 用于统计字符串 p 的字符频率
s_count = [0] * 26
p_count = [0] * 26

# 遍历 p 的前 p_len 个字符,初始化 s_count 和 p_count
for i in range(p_len):
# 统计 s 的前 p_len 个字符的频率
s_count[ord(s[i]) - 97] += 1
# 统计 p 的前 p_len 个字符的频率
p_count[ord(p[i]) - 97] += 1

# 如果初始窗口的字符频率与 p 的字符频率相等,说明第一个窗口就是异位词
# 将起始索引 0 加入结果列表
if s_count == p_count:
ans.append(0)

# 滑动窗口遍历 s 的剩余部分
for i in range(s_len - p_len):
# 窗口滑动时,移除窗口左侧的字符,将其频率减 1
s_count[ord(s[i]) - 97] -= 1
# 添加窗口右侧的新字符,将其频率加 1
s_count[ord(s[i + p_len]) - 97] += 1

# 检查当前窗口的字符频率是否与 p 的字符频率相等
# 如果相等,说明当前窗口是异位词,将起始索引(i + 1)加入结果列表
if s_count == p_count:
ans.append(i + 1)

# 返回结果列表
return ans

子串

  • 和为 k的子数组

    给你一个整数数组 nums 和一个整数 k ,请你统计并返回 该数组中和为 k 的子数组的个数

    子数组是数组中元素的连续非空序列。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    class Solution:
    def subarraySum(self, nums: List[int], k: int) -> int:
    count, pre = 0, 0
    hashmap = dict({0:1})

    for num in nums:
    pre += num
    if pre - k in hashmap:
    count += hashmap[pre - k]

    if pre in hashmap:
    hashmap[pre] += 1
    else:
    hashmap[pre] = 1

    return count
  • 滑动窗口最大值

给你一个整数数组 nums,有一个大小为 k 的滑动窗口从数组的最左侧移动到数组的最右侧。你只可以看到在滑动窗口内的 k 个数字。滑动窗口每次只向右移动一位。

返回 滑动窗口中的最大值

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
39
40
class Solution:
def maxSlidingWindow(self, nums: List[int], k: int) -> List[int]:
# 如果数组为空或窗口大小为0,直接返回空列表
if not nums or k == 0: return []

# 使用双端队列来存储当前窗口中的元素索引
deque = collections.deque()

# 未形成窗口时的处理
for i in range(k):
# 如果队列不为空且队列末尾的元素小于当前元素,则弹出队列末尾的元素
# 这样可以保证队列中的元素是递减的
while deque and deque[-1] < nums[i]:
deque.pop()
# 将当前元素加入队列
deque.append(nums[i])

# 将当前窗口的最大值(即队列的第一个元素)加入结果列表
res = [deque[0]]

# 形成窗口后的处理
for i in range(k, len(nums)):
# 如果队列的第一个元素是窗口最左边的元素,则将其从队列中移除
# 因为这个元素即将离开窗口
if deque[0] == nums[i - k]:
deque.popleft()

# 如果队列不为空且队列末尾的元素小于当前元素,则弹出队列末尾的元素
# 这样可以保证队列中的元素是递减的
while deque and deque[-1] < nums[i]:
deque.pop()

# 将当前元素加入队列
deque.append(nums[i])

# 将当前窗口的最大值(即队列的第一个元素)加入结果列表
res.append(deque[0])

# 返回结果列表
return res
  • 最小覆盖字串

给你一个字符串 s 、一个字符串 t 。返回 s 中涵盖 t 所有字符的最小子串。如果 s 中不存在涵盖 t 所有字符的子串,则返回空字符串 ""

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
class Solution:
def minWindow(self, s: str, t: str) -> str:
ans_left, ans_right = -1, len(s)
cnt = defaultdict(int) # 比 Counter 更快
for c in t:
cnt[c] += 1
less = len(cnt)

left = 0
for right, c in enumerate(s):
cnt[c] -= 1
if cnt[c] == 0:
less -= 1
while less == 0:
if right - left < ans_right - ans_left:
ans_left, ans_right = left, right
x = s[left]
if cnt[x] == 0:
less += 1
cnt[x] += 1
left += 1

return "" if ans_left < 0 else s[ans_left: ans_right + 1]

普通数组

  • 最大子数组和

给你一个整数数组 nums ,请你找出一个具有最大和的连续子数组(子数组最少包含一个元素),返回其最大和。

子数组是数组中的一个连续部分。

1
2
3
4
5
6
7
8
9
10
11
12
class Solution:
def maxSubArray(self, nums: List[int]) -> int:
count = 0
result = -99999
for i in nums:
count += i
if count > result:
result = count
if count < 0:
count = 0

return result
  • 合并区间

以数组 intervals 表示若干个区间的集合,其中单个区间为 intervals[i] = [starti, endi] 。请你合并所有重叠的区间,并返回 一个不重叠的区间数组,该数组需恰好覆盖输入中的所有区间

1
2
3
4
5
6
7
8
9
10
11
12
13
14
class Solution:
def merge(self, intervals: List[List[int]]) -> List[List[int]]:
intervals.sort(key=lambda x: x[0])

merged = []
for interval in intervals:
# 如果列表为空,或者当前区间与上一区间不重合,直接添加
if not merged or merged[-1][1] < interval[0]:
merged.append(interval)
else:
# 否则的话,我们就可以与上一区间进行合并
merged[-1][1] = max(merged[-1][1], interval[1])

return merged
  • 轮转数组

给定一个整数数组 nums,将数组中的元素向右轮转 k 个位置,其中 k 是非负数。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
class Solution:
def rotate(self, nums: List[int], k: int) -> None:
def reverse(i: int, j: int) -> None:
while i < j:
nums[i], nums[j] = nums[j], nums[i]
i += 1
j -= 1

n = len(nums)
k %= n # 轮转 k 次等于轮转 k % n 次
reverse(0, n - 1)
reverse(0, k - 1)
reverse(k, n - 1)

  • 除自身以外数组的乘积

给你一个整数数组 nums,返回 数组 answer ,其中 answer[i] 等于 nums 中除 nums[i] 之外其余各元素的乘积 。

题目数据 保证 数组 nums之中任意元素的全部前缀元素和后缀的乘积都在 32 位 整数范围内。

请 **不要使用除法,**且在 O(n) 时间复杂度内完成此题。

1
2
3
4
5
6
7
8
9
10
11
12
13
import math

class Solution:
def productExceptSelf(self, nums: List[int]) -> List[int]:
# 如果有0,可以简化运算
total = math.prod(nums)
ans = []
for i, num in enumerate(nums):
if num != 0:
ans.append(total // num)
else:
ans.append(math.prod(nums[:i] + nums[i+1:]))
return ans
  • 缺失的第一个正数

给你一个未排序的整数数组 nums ,请你找出其中没有出现的最小的正整数。

请你实现时间复杂度为 O(n) 并且只使用常数级别额外空间的解决方案。

1
2
3
4
5
6
7
8
9
10
11
12
13
class Solution:
def firstMissingPositive(self, nums: List[int]) -> int:
N = len(nums)
num_list = [0]*(N+1)
for num in nums:
if num>N or num<=0:
continue
num_list[num] = 1

for i in range(1,N+1):
if num_list[i] == 0:
return i
return N+1

矩阵

  • 矩阵置零

给定一个 *m* x *n* 的矩阵,如果一个元素为 0 ,则将其所在行和列的所有元素都设为 0 。请使用 原地 算法**。**

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
class Solution:
def setZeroes(self, matrix: List[List[int]]) -> None:
"""
Do not return anything, modify matrix in-place instead.
"""
m, n = len(matrix), len(matrix[0])
row = [False] * m
col = [False] * n

for i in range(m):
for j in range(n):
if matrix[i][j] == 0:
row[i], col[j] = True, True

for i in range(m):
for j in range(n):
if row[i] or col[j]:
matrix[i][j] = 0
  • 螺旋矩阵

给你一个 mn 列的矩阵 matrix ,请按照 顺时针螺旋顺序 ,返回矩阵中的所有元素。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
class Solution:
def spiralOrder(self, matrix: List[List[int]]) -> List[int]:
result = []
while matrix :
result.extend(matrix.pop(0))
if matrix :
if matrix[0]:
for i in range(len(matrix)):
result.append(matrix[i].pop())
if matrix :
k = matrix.pop()
result.extend(k[::-1])
if matrix :
if matrix[0]:
for i in range(len(matrix)-1,-1,-1):
result.append(matrix[i].pop(0))
return result
  • 旋转图像

给定一个 n × n 的二维矩阵 matrix 表示一个图像。请你将图像顺时针旋转 90 度。

你必须在** 原地** 旋转图像,这意味着你需要直接修改输入的二维矩阵。请不要 使用另一个矩阵来旋转图像。

1
2
3
4
5
6
7
8
9
10
class Solution:
def rotate(self, matrix: List[List[int]]) -> None:
n = len(matrix)
# 深拷贝 matrix -> tmp
tmp = copy.deepcopy(matrix)
# 根据元素旋转公式,遍历修改原矩阵 matrix 的各元素
for i in range(n):
for j in range(n):
matrix[j][n - 1 - i] = tmp[i][j]

链表

  • 相交链表

给你两个单链表的头节点 headAheadB ,请你找出并返回两个单链表相交的起始节点。如果两个链表不存在相交节点,返回 null

1
2
3
4
5
6
7
class Solution:
def getIntersectionNode(self, headA: ListNode, headB: ListNode) -> ListNode:
A, B = headA, headB
while A != B:
A = A.next if A else headB
B = B.next if B else headA
return A
  • 反转链表

给你单链表的头节点 head ,请你反转链表,并返回反转后的链表。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
# 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]:
cur ,pre = head, None
while cur:
temp = cur.next
cur.next = pre
pre = cur
cur = temp
return pre

二叉树

  • 二叉树的中序遍历

给定一个二叉树的根节点 root ,返回 它的 中序 遍历

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
class Solution:
def inorderTraversal(self, root: TreeNode) -> List[int]:
# 初始化栈和结果列表
stack, rst = [root], [] # stack 用于模拟递归调用栈,rst 用于存储遍历结果

# 开始遍历
while stack: # 当栈不为空时,继续遍历
i = stack.pop() # 弹出栈顶元素

# 如果当前元素是 TreeNode 类型
if isinstance(i, TreeNode):
# 将右子树、当前节点的值、左子树按顺序压入栈
stack.extend([i.right, i.val, i.left]) # 注意顺序:右 -> 值 -> 左

# 如果当前元素是整数类型(节点的值)
elif isinstance(i, int):
rst.append(i) # 将节点的值加入结果列表

# 返回结果列表
return rst
  • 二叉树的最大深度

给定一个二叉树 root ,返回其最大深度。二叉树的 最大深度 是指从根节点到最远叶子节点的最长路径上的节点数。

1
2
3
4
class Solution:
def maxDepth(self, root: Optional[TreeNode]) -> int:
if not root: return 0
return max(self.maxDepth(root.left),self.maxDepth(root.right))+1

图论

  • 岛屿数量

给你一个由 '1'(陆地)和 '0'(水)组成的的二维网格,请你计算网格中岛屿的数量。岛屿总是被水包围,并且每座岛屿只能由水平方向和/或竖直方向上相邻的陆地连接形成。此外,你可以假设该网格的四条边均被水包围。

1
2
3
4
5
6
7
输入:grid = [
["1","1","0","0","0"],
["1","1","0","0","0"],
["0","0","1","0","0"],
["0","0","0","1","1"]
]
输出:3
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
class Solution:
def numIslands(self, grid: List[List[str]]) -> int:
n_l = 0 # 初始化岛屿数量为 0

# 遍历整个网格
for i in range(len(grid)):
for j in range(len(grid[0])):
# 如果当前单元格是陆地('1'),则开始 DFS 遍历
if grid[i][j] == '1':
self.landpaint(grid, i, j) # 标记当前岛屿的所有陆地
n_l += 1 # 岛屿数量加 1

# 返回岛屿数量
return n_l

# DFS 函数:标记当前岛屿的所有陆地
def landpaint(self, grid, x, y):
# 将当前陆地标记为已访问('2')
grid[x][y] = '2'

# 检查下方单元格
if x + 1 < len(grid) and grid[x + 1][y] == '1':
self.landpaint(grid, x + 1, y)

# 检查右方单元格
if y + 1 < len(grid[0]) and grid[x][y + 1] == '1':
self.landpaint(grid, x, y + 1)

# 检查上方单元格
if x - 1 >= 0 and grid[x - 1][y] == '1':
self.landpaint(grid, x - 1, y)

# 检查左方单元格
if y - 1 >= 0 and grid[x][y - 1] == '1':
self.landpaint(grid, x, y - 1)
  • 腐烂的橘子

在给定的 m x n 网格 grid 中,每个单元格可以有以下三个值之一:值 0 代表空单元格;值 1 代表新鲜橘子;值 2 代表腐烂的橘子。

每分钟,腐烂的橘子 周围 4 个方向上相邻 的新鲜橘子都会腐烂。返回 直到单元格中没有新鲜橘子为止所必须经过的最小分钟数。如果不可能,返回 -1

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
class Solution:
def orangesRotting(self, grid: List[List[int]]) -> int:
m,n=len(grid),len(grid[0])
fresh=0
q=[]
for i,row in enumerate(grid):
for j,x in enumerate(row):
if x==1:
fresh+=1
elif x==2:
q.append((i,j))

ans=0
while q and fresh:
ans+=1
tmp=q
q=[]
for x,y in tmp:
for i, j in (x - 1, y), (x + 1, y), (x, y - 1), (x, y + 1): # 四方向
if 0 <= i < m and 0 <= j < n and grid[i][j] == 1: # 新鲜橘子
fresh -= 1
grid[i][j] = 2 # 变成腐烂橘子
q.append((i, j))
return -1 if fresh else ans

回溯

  • 全排列

给定一个不含重复数字的数组 nums ,返回其 所有可能的全排列 。你可以 按任意顺序 返回答案。

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
class Solution:
def permute(self, nums: List[int]) -> List[List[int]]:
# 定义 DFS 函数
def dfs(nums, size, depth, path, used, res):
# 如果当前深度等于数组大小,说明找到一个完整排列
if depth == size:
res.append(path[:]) # 将当前排列加入结果列表
return

# 遍历数组中的每个元素
for i in range(size):
# 检查当前元素是否已经被使用过
if not used[i]:
# 标记当前元素为已使用
used[i] = True
# 将当前元素加入路径
path.append(nums[i])
# 递归调用 DFS,继续生成下一个位置的元素
dfs(nums, size, depth + 1, path, used, res)
# 回溯:移除当前元素,恢复状态
path.pop()
used[i] = False

# 获取数组的长度
size = len(nums)
# 如果数组为空,直接返回空列表
if size == 0:
return []

# 初始化 used 数组和结果列表
used = [False] * size # 记录元素是否被使用
res = [] # 结果列表,用于存储所有排列

# 调用 DFS 函数,开始生成排列
dfs(nums, size, 0, [], used, res)

# 返回结果列表
return res
  • 子集

给你一个整数数组 nums ,数组中的元素 互不相同 。返回该数组所有可能的子集(幂集)。解集 不能 包含重复的子集。你可以按 任意顺序 返回解集。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
class Solution:
def subsets(self, nums: List[int]) -> List[List[int]]:
# 初始化结果列表
res = []
# 获取数组的长度
n = len(nums)

# 定义回溯函数
def helper(i, tmp):
# 将当前子集加入结果列表
res.append(tmp)
# 遍历数组,生成新的子集
for j in range(i, n):
# 递归调用,更新子集
helper(j + 1, tmp + [nums[j]])

# 调用回溯函数,从索引 0 和空子集开始
helper(0, [])
# 返回结果列表
return res

二分查找

  • 搜索插入位置

给定一个排序数组和一个目标值,在数组中找到目标值,并返回其索引。如果目标值不存在于数组中,返回它将会被按顺序插入的位置。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
class Solution:
def searchInsert(self, nums: List[int], target: int) -> int:
left, right = 0, len(nums) - 1

while left <= right:
mid = (left + right) // 2
if nums[mid] == target:
return mid
elif nums[mid] < target:
left = mid + 1
else:
right = mid - 1

return left # left 位置即为插入位置
  • 搜索二维矩阵

给你一个满足下述两条属性的 m x n 整数矩阵:每行中的整数从左到右按非严格递增顺序排列。每行的第一个整数大于前一行的最后一个整数。给你一个整数 target ,如果 target 在矩阵中,返回 true ;否则,返回 false

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
class Solution:
def searchMatrix(self, matrix: List[List[int]], target: int) -> bool:
# 获取矩阵的行数和列数
m, n = len(matrix), len(matrix[0])

# 初始化二分查找的左右边界
l, r = 0, m * n # 将二维矩阵视为一维数组

# 开始二分查找
while l < r:
# 计算中间位置
mid = (l + r) >> 1 # 等价于 (l + r) // 2
# 将一维索引 mid 转换为二维索引
x = matrix[mid // n][mid % n]

# 判断中间元素与目标值的关系
if x == target:
return True # 找到目标值,返回 True
if x < target:
l = mid + 1 # 目标值在右半部分
else:
r = mid # 目标值在左半部分

# 未找到目标值,返回 False
return False

  • 有效的括号

给定一个只包括 '('')''{''}''['']' 的字符串 s ,判断字符串是否有效。

有效字符串需满足:

  1. 左括号必须用相同类型的右括号闭合。

  2. 左括号必须以正确的顺序闭合。

  3. 每个右括号都有一个对应的相同类型的左括号。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    class Solution:
    def isValid(self, s: str) -> bool:
    dic = {')':'(',']':'[','}':'{'}
    stack = []
    for i in s:
    if stack and i in dic:
    if stack[-1] == dic[i]: stack.pop()
    else: return False
    else: stack.append(i)

    return not stack
  • 最小栈

设计一个支持 pushpoptop 操作,并能在常数时间内检索到最小元素的栈。实现 MinStack 类:

1
2
3
4
5
- `MinStack()` 初始化堆栈对象。
- `void push(int val)` 将元素val推入堆栈。
- `void pop()` 删除堆栈顶部的元素。
- `int top()` 获取堆栈顶部的元素。
- `int getMin()` 获取堆栈中的最小元素。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
class MinStack:

def __init__(self):
self.stack = []
self.min_stack = [math.inf]

def push(self, val: int) -> None:
self.stack.append(val)
self.min_stack.append(min(val,self.min_stack[-1]))

def pop(self) -> None:
self.stack.pop()
self.min_stack.pop()

def top(self) -> int:
return self.stack[-1]

def getMin(self) -> int:
return self.min_stack[-1]

  • 数组中的第k个最大元素

给定整数数组 nums 和整数 k,请返回数组中第 **k** 个最大的元素。请注意,你需要找的是数组排序后的第 k 个最大的元素,而不是第 k 个不同的元素。

你必须设计并实现时间复杂度为 O(n) 的算法解决此问题。

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
class Solution:
def findKthLargest(self, nums: List[int], k: int) -> int:
# 定义快速选择函数
def quick_select(nums, k):
# 随机选择一个基准元素(pivot)
pivot = random.choice(nums)

# 初始化三个列表/变量:
# - big:存储比 pivot 大的元素
# - equal:记录与 pivot 相等的元素个数
# - small:存储比 pivot 小的元素
big, equal, small = [], 0, []

# 遍历数组,将元素分类
for num in nums:
if num > pivot:
big.append(num) # 比 pivot 大的元素放入 big
elif num < pivot:
small.append(num) # 比 pivot 小的元素放入 small
else:
equal += 1 # 与 pivot 相等的元素计数

# 如果 k 小于等于 big 的长度,说明第 k 个最大元素在 big 中
if k <= len(big):
return quick_select(big, k) # 递归在 big 中查找

# 如果 k 大于 big 和 equal 的总长度,说明第 k 个最大元素在 small 中
if len(big) + equal < k:
return quick_select(small, k - len(big) - equal) # 递归在 small 中查找

# 如果以上条件都不满足,说明第 k 个最大元素就是 pivot
return pivot

# 调用快速选择函数,返回第 k 个最大元素
return quick_select(nums, k)
  • 前k个高频元素

给你一个整数数组 nums 和一个整数 k ,请你返回其中出现频率前 k 高的元素。你可以按 任意顺序 返回答案。

1
2
3
4
class Solution:
def topKFrequent(self, nums: List[int], k: int) -> List[int]:
count = collections.Counter(nums)
return [item[0] for item in count.most_common(k)]

贪心算法

  • 买卖股票的最佳时期

给定一个数组 prices ,它的第 i 个元素 prices[i] 表示一支给定股票第 i 天的价格。你只能选择 某一天 买入这只股票,并选择在 未来的某一个不同的日子 卖出该股票。设计一个算法来计算你所能获取的最大利润。返回你可以从这笔交易中获取的最大利润。如果你不能获取任何利润,返回 0

1
2
3
4
5
6
7
8
9
10
11
12
class Solution:
def maxProfit(self, prices: List[int]) -> int:
profit = 0
low=high=prices[0]
for p in prices:
if p < low: #对每个新的最低点来说,之前的最高点不再有效,需要结利
profit = max(profit, high-low)
low=high=p
elif p > high:
high=p
profit = max(profit, high-low)
return profit
  • 跳跃游戏

给你一个非负整数数组 nums ,你最初位于数组的 第一个下标 。数组中的每个元素代表你在该位置可以跳跃的最大长度。判断你是否能够到达最后一个下标,如果可以,返回 true ;否则,返回 false

1
2
3
4
5
6
7
8
9
class Solution:
def canJump(self, nums: List[int]) -> bool:
if not nums :return False
maxlong,n= 0,len(nums)
for i in range(n):
if i>maxlong:
return False
maxlong= max(i+nums[i],maxlong)
return True

动态规划

  • 爬楼梯

假设你正在爬楼梯。需要 n 阶你才能到达楼顶。每次你可以爬 12 个台阶。你有多少种不同的方法可以爬到楼顶呢?

1
2
3
4
5
6
class Solution:
def climbStairs(self, n: int) -> int:
a, b = 1, 1
for _ in range(n - 1):
a, b = b, a + b
return b
  • 杨辉三角

给定一个非负整数 *numRows,*生成「杨辉三角」的前 numRows 行。

1
2
3
4
5
6
7
8
class Solution:
def generate(self, numRows: int) -> List[List[int]]:
c = [[1] * (i + 1) for i in range(numRows)]
for i in range(2, numRows):
for j in range(1, i):
# 左上方的数 + 正上方的数
c[i][j] = c[i - 1][j - 1] + c[i - 1][j]
return c

多维动态规划

  • 不同路径

一个机器人位于一个 m x n 网格的左上角 (起始点在下图中标记为 “Start” )。机器人每次只能向下或者向右移动一步。机器人试图达到网格的右下角(在下图中标记为 “Finish” )。问总共有多少条不同的路径?

1
2
3
4
5
6
7
class Solution:
def uniquePaths(self, m: int, n: int) -> int:
f = [1] * n
for i in range(1, m):
for j in range(1, n):
f[j] += f[j - 1]
return f[n - 1]
  • 最小路径和

给定一个包含非负整数的 *m* x *n* 网格 grid ,请找出一条从左上角到右下角的路径,使得路径上的数字总和为最小。**说明:**每次只能向下或者向右移动一步。

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
class Solution:
def minPathSum(self, grid: List[List[int]]) -> int:
# 如果网格为空,返回 0
if not grid or not grid[0]:
return 0

# 获取网格的行数和列数
rows, columns = len(grid), len(grid[0])

# 初始化动态规划数组 dp
dp = [[0] * columns for _ in range(rows)]

# 起点的最小路径和就是 grid[0][0]
dp[0][0] = grid[0][0]

# 初始化第一列的最小路径和
for i in range(1, rows):
dp[i][0] = dp[i - 1][0] + grid[i][0]

# 初始化第一行的最小路径和
for j in range(1, columns):
dp[0][j] = dp[0][j - 1] + grid[0][j]

# 填充 dp 数组的其余部分
for i in range(1, rows):
for j in range(1, columns):
# 当前点的最小路径和等于上方或左方的最小路径和加上当前点的值
dp[i][j] = min(dp[i - 1][j], dp[i][j - 1]) + grid[i][j]

# 返回右下角的最小路径和
return dp[rows - 1][columns - 1]