AI 求职面试实战(二):Python/数据结构算法高频题
系列导读:本文是「AI 求职面试实战」系列第2期。第1期我们讲了 AI 岗位全景和简历优化,本期聚焦面试中最硬核的部分: : 手撕代码。
本期内容:Python 面试高频考点 + LeetCode 精选题型拆解 + AI 岗位专属算法策略 + 手撕代码高分模板。
一、AI 岗位算法面试的特殊性
1.1 不同方向对算法的要求
AI 岗位的算法面试并非一刀切,不同方向侧重点不同:
| 方向 | 算法难度 | 高频考点 | 注意事项 |
|----------------|--------------------|--------------------------------------------|----------------------------|
| 算法研究岗 | LeetCode Medium | 二分查找、BFS/DFS、动态规划 | 数学推导比代码风格更重要 |
| 算法工程岗 | LeetCode Medium-Hard | 排序、哈希、树、动态规划 | 注重代码质量和工程习惯 |
| AI 工程岗 | LeetCode Easy-Medium | 字符串处理、数组操作、基础数据结构 | 更看重工程能力和系统设计 |
| AI 应用开发岗 | LeetCode Easy-Medium | 哈希表、数组、字符串 | 更看重 LLM 应用和 Prompt |一条规律:大厂(字节/阿里/腾讯/快手)算法面通常覆盖到 Medium-Hard,创业公司更多 Easy-Medium。研究岗重思维,工程岗重代码质量。
1.2 AI 岗算法面试的"潜规则"
规则1:时间优先,空间其次
绝大多数面试官先看你能不能做出来,再考虑优化。能写出 O(n) 解就是 60 分,O(n log n) 也可以接受,卡在 O(n²) 可能挂。空间换时间是被鼓励的。
规则2:代码风格反映工程习惯
变量命名、函数拆分、边界检查这些细节,面试官很在意。一个变量名叫
a和一个叫window_left,高下立判。
规则3:沟通比沉默重要
面试官不怕你慢,怕你一声不吭写 20 分钟然后写出个错的。边写边讲思路,写错了面试官还能给提示。
规则4:AI 岗喜欢跟具体场景结合的题
同样一道排序题,面试官可能问"如果这个排序在推荐系统的实时特征计算中使用,你有什么考虑?"
二、Python 高频面试考点
2.1 Python 基础必备
Q1:列表推导式与生成器
❌ 面试官问:"你平时怎么创建一个平方数列表?"
✅ 低分回答:
squares = []
for i in range(10):
squares.append(i * i)✅ 高分回答:
# 列表推导式
squares = [i * i for i in range(10)]
# 如果数据量大,用生成器省内存
squares_gen = (i * i for i in range(1000000))Q2:可变对象与不可变对象
这是 Python 面试最高频的"坑题"之一。
# 不可变:int, str, tuple, frozenset
# 可变:list, dict, set
def add_item(item, items=[]):
items.append(item)
return items
print(add_item(1)) # [1]
print(add_item(2)) # [1, 2] ← 注意!不是 [2]
print(add_item(3)) # [1, 2, 3]✅ 正确的做法:
def add_item(item, items=None):
if items is None:
items = []
items.append(item)
return itemsQ3:浅拷贝 vs 深拷贝
import copy
original = [[1, 2], [3, 4]]
shallow = copy.copy(original) # 外层是新对象,内层还是引用
deep = copy.deepcopy(original) # 完全独立的新对象
shallow[0][0] = 99
print(original) # [[99, 2], [3, 4]] ← 被改了!
deep[0][0] = 42
print(original) # [[99, 2], [3, 4]] ← 没影响2.2 高频内置函数
Q4:map、filter、reduce 的用法
from functools import reduce
# map:映射
nums = [1, 2, 3, 4, 5]
squared = list(map(lambda x: x**2, nums)) # [1, 4, 9, 16, 25]
# filter:过滤
evens = list(filter(lambda x: x % 2 == 0, nums)) # [2, 4]
# reduce:累积
sum_all = reduce(lambda x, y: x + y, nums) # 15面试延伸:Python 3 中
reduce被移到了functools: : 面试官可能会问为什么。答:因为reduce可读性差,能用 for 循环的地方应优先用 for 循环。
Q5:sorted vs list.sort
# list.sort() : : 原地排序,返回 None
nums = [3, 1, 4, 1, 5]
nums.sort() # nums 变成 [1, 1, 3, 4, 5]
# sorted() : : 返回新列表,原列表不变
nums = [3, 1, 4, 1, 5]
sorted_nums = sorted(nums) # nums 不变
# 自定义排序
sorted_by_length = sorted(['aa', 'b', 'ccc'], key=lambda x: len(x))
# ['b', 'aa', 'ccc']2.3 高阶考点
Q6:装饰器原理
from functools import wraps
import time
# 一个实用的计时装饰器
def timer(func):
@wraps(func)
def wrapper(*args, **kwargs):
start = time.time()
result = func(*args, **kwargs)
end = time.time()
print(f"{func.__name__} took {end - start:.4f}s")
return result
return wrapper
@timer
def slow_function():
time.sleep(0.5)
slow_function() # slow_function took 0.5012s面试题变体:写一个带参数的装饰器,比如
@retry(times=3)。
def retry(times=3):
def decorator(func):
@wraps(func)
def wrapper(*args, **kwargs):
for i in range(times):
try:
return func(*args, **kwargs)
except Exception as e:
if i == times - 1:
raise
print(f"Retry {i+1}/{times}: {e}")
return None
return wrapper
return decoratorQ7:__slots__ 的作用
面试常见追问:如何优化大量 Python 对象的内存占用?
# 不使用 __slots__
class Point:
def __init__(self, x, y):
self.x = x
self.y = y
# 使用 __slots__
class PointOptimized:
__slots__ = ['x', 'y']
def __init__(self, x, y):
self.x = x
self.y = y
import sys
p1 = Point(1, 2)
p2 = PointOptimized(1, 2)
print(sys.getsizeof(p1)) # 56 (包含 __dict__)
print(sys.getsizeof(p2)) # 40 (没有 __dict__)内存节省:每个对象减少约 16 字节(Python 3.8+),百万级对象可节省十几 MB。
Q8:上下文管理器(with 语句)
# 手动实现
class FileManager:
def __init__(self, filename, mode):
self.filename = filename
self.mode = mode
def __enter__(self):
self.file = open(self.filename, self.mode)
return self.file
def __exit__(self, exc_type, exc_val, exc_tb):
self.file.close()
# 返回 True 会抑制异常
return False
# 使用 contextlib
from contextlib import contextmanager
@contextmanager
def file_manager(filename, mode):
f = open(filename, mode)
try:
yield f
finally:
f.close()Q9:多线程与多进程
AI 岗最常见的问题:
# GIL 限制了多线程的并行性
# CPU 密集型 -> 多进程
# IO 密集型 -> 多线程
from multiprocessing import Pool
def process_data(x):
return x * x
with Pool(4) as pool:
results = pool.map(process_data, range(100))面试高频追问:Python 的 GIL 是什么?为什么有了 GIL 还要有线程锁?Numpy 为什么不受 GIL 限制?
三、LeetCode 高频题型深度拆解
3.1 数组与哈希表
题型1:两数之和(Two Sum)
这是 LeetCode 第1题,面试出现的频率也是第1,几乎必考。
def twoSum(nums, target):
"""
给定一个整数数组和一个目标值,找出数组中和为目标值的两个数的下标。
假设每种输入只对应一个答案,且不能重复使用同一元素。
示例:nums = [2, 7, 11, 15], target = 9 -> [0, 1]
"""
seen = {}
for i, num in enumerate(nums):
complement = target - num
if complement in seen:
return [seen[complement], i]
seen[num] = i
return []时间复杂度:O(n),空间复杂度:O(n)
面试追问:如果数组是排序的,能否优化空间?→ 可以用双指针,空间 O(1)
变体1:三数之和(3Sum)→ 排序 + 双指针,O(n²)
变体2:四数之和(4Sum)→ 两层循环 + 双指针,O(n³)
AI 岗延伸:在推荐系统的协同过滤中,两数之和的思想可以用于"找出评分模式相似的用户"。
题型2:最长连续序列
def longestConsecutive(nums):
"""
给定一个未排序的整数数组,找出最长连续序列的长度。
要求 O(n) 时间复杂度。
示例:[100, 4, 200, 1, 3, 2] -> 4 (1,2,3,4)
"""
num_set = set(nums)
longest = 0
for num in num_set:
# 只从连续序列的起点开始检查
if num - 1 not in num_set:
current_num = num
current_length = 1
while current_num + 1 in num_set:
current_num += 1
current_length += 1
longest = max(longest, current_length)
return longest时间复杂度:O(n),空间复杂度:O(n)。每个元素只会被访问两次(起点检查 + 连续延伸)。
AI 岗延伸:这个思路在特征工程中有用: : 找到连续的 token ID、连续的时间戳序列等。
题型3:接雨水
大厂常考 Hard 题:
def trap(height):
"""
给定 n 个非负整数表示每个宽度为 1 的柱子的高度图,
计算按此排列的柱子,下雨之后能接多少雨水。
示例:[0,1,0,2,1,0,1,3,2,1,2,1] -> 6
"""
if not height:
return 0
left, right = 0, len(height) - 1
left_max = right_max = 0
water = 0
while left < right:
if height[left] < height[right]:
if height[left] >= left_max:
left_max = height[left]
else:
water += left_max - height[left]
left += 1
else:
if height[right] >= right_max:
right_max = height[right]
else:
water += right_max - height[right]
right -= 1
return water时间复杂度:O(n),空间复杂度:O(1)。双指针法。
面试技巧:这道题的核心思想是"每个位置能接的雨水 = min(左边最高, 右边最高) - 当前高度"。用双指针可以避免额外空间。
3.2 滑动窗口
题型4:无重复字符的最长子串
def lengthOfLongestSubstring(s):
"""
给定一个字符串,找出不含重复字符的最长连续子串的长度。
示例:"abcabcbb" -> 3 ("abc")
"""
char_map = {}
left = 0
max_len = 0
for right, char in enumerate(s):
if char in char_map and char_map[char] >= left:
left = char_map[char] + 1
char_map[char] = right
max_len = max(max_len, right - left + 1)
return max_len时间复杂度:O(n),空间复杂度:O(min(m, n)),m 是字符集大小。
AI 岗延伸:滑动窗口在 NLP 中有广泛应用: : 滑动窗口生成训练样本、N-gram 特征提取、文本分割等。
3.3 二分查找
题型5:搜索旋转排序数组
def search(nums, target):
"""
在旋转排序数组中搜索目标值。
示例:[4,5,6,7,0,1,2], target=0 -> 4 (index)
"""
left, right = 0, len(nums) - 1
while left <= right:
mid = (left + right) // 2
if nums[mid] == target:
return mid
# 左半有序
if nums[left] <= nums[mid]:
if nums[left] <= target < nums[mid]:
right = mid - 1
else:
left = mid + 1
# 右半有序
else:
if nums[mid] < target <= nums[right]:
left = mid + 1
else:
right = mid - 1
return -1时间复杂度:O(log n)。关键是判断哪一边是有序的。
变体:寻找旋转点、搜索旋转数组的最小值。
3.4 树与二叉树
题型6:二叉树层序遍历
from collections import deque
def levelOrder(root):
"""
二叉树的层序遍历(BFS)。
"""
if not root:
return []
result = []
queue = deque([root])
while queue:
level_size = len(queue)
current_level = []
for _ in range(level_size):
node = queue.popleft()
current_level.append(node.val)
if node.left:
queue.append(node.left)
if node.right:
queue.append(node.right)
result.append(current_level)
return result时间复杂度:O(n),空间复杂度:O(n)。
AI 岗延伸:树的遍历思想在 Transformer 的注意力机制中有体现: : Tree Attention 等。
题型7:验证二叉搜索树
def isValidBST(root):
"""
判断一棵树是否是二叉搜索树。
核心:中序遍历必须严格递增。
"""
def inorder(node, prev):
if not node:
return True
if not inorder(node.left, prev):
return False
if prev[0] is not None and node.val <= prev[0]:
return False
prev[0] = node.val
return inorder(node.right, prev)
prev = [None]
return inorder(root, prev)易错点:不能只检查左右子节点与根节点的大小关系,必须保证整个左子树都小于根节点。
3.5 图论
题型8:课程表(拓扑排序)
from collections import deque
def canFinish(numCourses, prerequisites):
"""
课程表:判断是否可以完成所有课程的学习。
prerequisites[i] = [ai, bi] 表示学 ai 之前必须先学 bi
本质:检测有向图是否有环。
"""
# 建图 + 入度表
graph = [[] for _ in range(numCourses)]
in_degree = [0] * numCourses
for course, prereq in prerequisites:
graph[prereq].append(course)
in_degree[course] += 1
# 入度为 0 的节点入队
queue = deque([i for i in range(numCourses) if in_degree[i] == 0])
count = 0
while queue:
node = queue.popleft()
count += 1
for neighbor in graph[node]:
in_degree[neighbor] -= 1
if in_degree[neighbor] == 0:
queue.append(neighbor)
return count == numCourses时间复杂度:O(V + E),V 是课程数,E 是依赖数。
AI 岗延伸:拓扑排序在 DAG(有向无环图)计算中有广泛应用: : 神经网络计算图、任务编排、依赖解析等。
3.6 动态规划
题型9:最长递增子序列
def lengthOfLIS(nums):
"""
最长递增子序列的长度。
示例:[10,9,2,5,3,7,101,18] -> 4 ([2,3,7,101])
"""
import bisect
tails = []
for num in nums:
# 二分查找,找到第一个不小于 num 的位置
pos = bisect.bisect_left(tails, num)
if pos == len(tails):
tails.append(num)
else:
tails[pos] = num
return len(tails)时间复杂度:O(n log n)(二分优化),空间复杂度:O(n)。
说明:
tails数组维护了长度为 i+1 的递增子序列的最小末尾值。
题型10:编辑距离
经典 NLP 相关 DP 题:
def minDistance(word1, word2):
"""
编辑距离:将 word1 转换为 word2 所需的最小操作数。
操作:插入、删除、替换。
"""
m, n = len(word1), len(word2)
dp = [[0] * (n + 1) for _ in range(m + 1)]
# 初始化边界
for i in range(m + 1):
dp[i][0] = i
for j in range(n + 1):
dp[0][j] = j
for i in range(1, m + 1):
for j in range(1, n + 1):
if word1[i-1] == word2[j-1]:
dp[i][j] = dp[i-1][j-1]
else:
dp[i][j] = min(
dp[i-1][j] + 1, # 删除
dp[i][j-1] + 1, # 插入
dp[i-1][j-1] + 1 # 替换
)
return dp[m][n]空间优化:可以用 2 行替代完整 DP 表,空间 O(min(m, n))。
AI 岗延伸:编辑距离是文本相似度计算的基础(拼写纠错、模糊匹配、BPE 分词中的 merge 策略)。
3.7 位运算
题型11:只出现一次的数字
def singleNumber(nums):
"""
给定一个非空整数数组,除了某个元素只出现一次外,其余都出现两次。
找出那个只出现一次的元素。
"""
result = 0
for num in nums:
result ^= num # XOR:相同为0,不同为1
return result时间复杂度:O(n),空间复杂度:O(1)。利用 XOR 的性质:a ^ a = 0, a ^ 0 = a。
AI 岗延伸:位运算在模型量化、Embedding 压缩中有应用。
四、AI 岗算法面试"加分项"
4.1 写出边界检查
面试官非常在意你是否考虑边界情况:
def binary_search(arr, target):
# ❌ 缺少边界检查
left, right = 0, len(arr) - 1
# ✅ 完善的边界检查
if not arr:
return -1
if target < arr[0] or target > arr[-1]:
return -1
# ... 主逻辑4.2 写出时间复杂度分析
写完代码后主动说:
"这个算法的时间复杂度是 O(n),空间复杂度是 O(1)。" "如果数据量很大(>1 亿),这个 O(n²) 的解法不可行,可以考虑用哈希表优化到 O(n)。"
4.3 写出空间优化
# 原始 DP:O(n²) 空间
dp = [[0] * n for _ in range(n)]
# 优化后:O(n) 空间
dp = [0] * n4.4 与 AI 场景结合
面试官面 AI 岗,天然期待你能把算法和自己的领域结合:
"这个二分查找的思路,可以类比超参数搜索中的贝叶斯优化: : 都是在搜索空间中高效找到最优解。" "滑动窗口的思想和 NLP 中的 N-gram 窗口提取是类似的,只是 NLP 中窗口大小固定,但步长可能不同。"
五、刷题策略与推荐题单
5.1 优先级策略
第一阶段(必备 30 题): : 1-2 周
├─ 数组:Two Sum(#1)、三数之和(#15)、最长连续序列(#128)
├─ 链表:反转链表(#206)、环形链表(#141)、合并有序链表(#21)
├─ 栈/队列:有效括号(#20)、最小栈(#155)、用栈实现队列(#232)
├─ 二叉树:层序遍历(#102)、验证 BST(#98)、最大深度(#104)
├─ 二分查找:搜索旋转数组(#33)、寻找峰值(#162)
└─ 动态规划:爬楼梯(#70)、最长递增子序列(#300)、编辑距离(#72)
第二阶段(进阶 20 题): : 第3周
├─ 图:课程表(#207)、岛屿数量(#200)
├─ 滑动窗口:无重复最长子串(#3)、最小覆盖子串(#76)
├─ 动态规划:接雨水(#42)、正则匹配(#10)
└─ 设计:LRU Cache(#146)、前缀树(#208)
第三阶段(目标公司专项): : 第4周
├─ 字节高频:接雨水、K个一组翻转链表、最长回文子串
├─ 腾讯高频:两数相加、螺旋矩阵、字符串解码
└─ 微软高频:LRU Cache、二叉树的最近公共祖先、多数元素5.2 刷题方法
正确姿势:一天 2-3 题,精做而非泛做
精做题的 step:
1. 独立写 20 分钟(不翻答案)
2. 提交运行(不管过没过)
3. 看最优解(LeetCode 讨论区 + 题解)
4. 用自己的风格重写一遍
5. 总结到错题本(核心思路 + 易错点 + 变体)错误的姿势:一天刷 20 题,每道题看答案"理解了"就过。
5.3 错题本模板
## 题目:接雨水(#42)
**核心思路**:双指针法,每个位置能接的水 = min(left_max, right_max) - height[i]
**易错点**:
- 双指针移动条件是 height[left] < height[right]
- 先更新 max,再计算 water(顺序不能反)
**变体**:
- 二维接雨水(#407)→ 堆 + BFS
- 盛最多水的容器(#11)→ 贪心双指针
**场景应用**:推荐系统的特征平滑、时间序列的去噪处理六、面试现场模拟
6.1 面试官给你的 20 分钟,怎么分配?
0:00 - 2:00 理解题意 + 确认边界条件
"输入范围是什么?空数组怎么办?返回什么类型?"
"可以假设输入合法吗?"
2:00 - 5:00 提出思路 + 和面试官确认
"我有两种思路:暴力解 O(n²) 和哈希表 O(n)。"
"面试官,我可以用哈希表吗?"
5:00 - 15:00 写代码
"我来写一下代码。"
"这里我创建一个哈希表用于记录..."
15:00 - 18:00 测试 + 排查
"我用一个例子跑一下:输入 [2,7,11,15], target=9"
"第一步看到 2,哈希表是空...第二步看到 7..."
18:00 - 20:00 复杂度分析 + 优化讨论
"时间复杂度 O(n),空间复杂度 O(n)。"
"如果面试官问能否优化空间,可以说用双指针..."6.2 常见翻车现场及应对
翻车1:题目读错了
不要慌,立即说:"抱歉,我刚才理解有误,让我重新看一下。" 重读题目,理解清楚再写。
翻车2:写到一半发现思路不对
"这个方法好像有问题,让我换个思路。" 擦掉重写,干净利落。面试官更看重你的反应而不是你一开始的完美。
翻车3:语法忘记/API 记不清
"Python 里这个 API 我有点不确定,是 extend 还是 append?" 直接问面试官,大多数面试官会告诉你。
翻车4:写完后发现 bug
"这里有一个边界情况没处理好,让我改一下。" 当场修复,然后说"我刚才漏掉了空数组的情况"。
七、本期小结与下期预告
核心要点
- Python 面试:列表推导、可变对象、装饰器、GIL 是高危考点
- 算法高频:Two Sum、滑动窗口、二分查找、二叉树遍历、拓扑排序、编辑距离: : 这6类几乎必考
- 加分技巧:边界检查、时间空间分析、与 AI 场景结合
- 刷题方法:一天 2-3 题精做 > 一天 20 题泛做
- 面试节奏:2分钟理解 → 3分钟讲思路 → 10分钟写代码 → 3分钟检查 → 2分钟分析
下期预告
第3期:机器学习面试题精讲 : LR/SVM/树模型/XGBoost 高频考点 + 手推公式 + 面试官追问拆解 + 场景回答模板。
📌 本系列其他文章 - 第1期:AI 岗位全景与简历突围 ✅ - 第2期:Python/数据结构算法高频题 ✅ (本文) - 第3期:机器学习面试题精讲(即将发布) - 第4期:深度学习 & LLM 面试题 - 第5期:系统设计面试(推荐/搜索/NLP) - 第6期:AI 工程面试:模型部署与优化 - 第7期:ML 算法场景题 & 业务分析 - 第8期:面试全流程实战 & 面经汇总
如果你觉得本文有帮助,欢迎点赞和分享,让更多备战 AI 面试的同学看到 🙌
夜雨聆风