培养程序化思维

从思维到落地

刷 LeetCode 不是为了背题,而是训练一种能力------把模糊的问题拆解成清晰的步骤,再用最合适的工具把它实现出来。本文结合算法思维与 Python 特性,帮你建立一套"想得清楚、写得出来、跑得够快"的刷题方法论。


1. 算法思维:七种武器

LeetCode 或者面试题目千变万化,但背后的思维方式可以归纳为七类。掌握这些"武器",你就能应对绝大多数问题。

1.1. 空间换时间:哈希表的艺术

核心洞察:计算机最擅长的事情是"查表",而不是"逐个比对"。

python 复制代码
# 两数之和:O(n²) 暴力解法 → O(n) 哈希解法
def two_sum(nums, target):
    seen = {}                    # 值 → 索引的映射
    for i, num in enumerate(nums):
        complement = target - num
        if complement in seen:   # O(1) 查找
            return [seen[complement], i]
        seen[num] = i
    return []

适用场景:需要频繁查找、统计频率、判断存在性的地方,第一时间想到哈希表。

1.2. 双指针与滑动窗口:让遍历不再暴力

核心洞察:很多遍历问题不需要两层循环,两个指针协同移动就能覆盖所有有效情况。

python 复制代码
# 无重复字符的最长子串:滑动窗口
def longest_substring(s):
    window = set()
    left = max_len = 0
    for right in range(len(s)):
        while s[right] in window:   # 收缩窗口
            window.remove(s[left])
            left += 1
        window.add(s[right])
        max_len = max(max_len, right - left + 1)
    return max_len

变体区分:

  • 左右指针:从两端向中间逼近(反转、盛水容器)
  • 快慢指针:处理链表环、数组原地修改
  • 滑动窗口:寻找满足条件的连续子区间

1.3. 递归、回溯与分治:化繁为简

核心洞察:大问题如果和小问题是同构的,就递归;如果需要在过程中"试错并回退",就回溯。

python 复制代码
# 全排列:回溯 = DFS + 状态重置
def permute(nums):
    def backtrack(path, used):
        if len(path) == len(nums):
            res.append(path[:])
            return
        for i in range(len(nums)):
            if not used[i]:
                used[i] = True
                path.append(nums[i])
                backtrack(path, used)
                path.pop()        # 撤销选择
                used[i] = False   # 状态重置
    
    res = []
    backtrack([], [False] * len(nums))
    return res

1.4. DFS 与 BFS:搜索的两面

维度 DFS BFS
实现方式 递归 / 栈 队列
特点 一条路走到底 一层一层扩散
适用场景 所有路径、连通性 最短路径(无权图)
python 复制代码
from collections import deque

# BFS 层序遍历:用 deque 保证 O(1) 的头尾操作
def level_order(root):
    if not root: return []
    q = deque([root])
    result = []
    while q:
        level = []
        for _ in range(len(q)):
            node = q.popleft()
            level.append(node.val)
            if node.left: q.append(node.left)
            if node.right: q.append(node.right)
        result.append(level)
    return result

1.5. 动态规划:记忆化 + 状态转移

核心洞察:如果一个问题可以被拆成重叠子问题,并且子问题的解可以复用,那就用 DP。

四步法:定义状态 → 找转移方程 → 初始化 → 确定遍历顺序

python 复制代码
# 打家劫舍:不相邻元素的最大和
def rob(nums):
    prev, curr = 0, 0
    for num in nums:
        prev, curr = curr, max(curr, prev + num)
    return curr

1.6. 贪心算法:每一步都选当前最优

核心洞察:如果局部最优能推导出全局最优,就不需要穷举所有可能性。

python 复制代码
# 分发饼干:用最小的饼干满足最小胃口的孩子
def find_content_children(g, s):
    g.sort()
    s.sort()
    i = j = 0
    while i < len(g) and j < len(s):
        if s[j] >= g[i]:
            i += 1
        j += 1
    return i

1.7. 二分查找:有序世界的减半魔法

核心洞察:只要数据具备"单调性",就可以用二分将 O(n) 降到 O(log n)。

python 复制代码
def binary_search(nums, target):
    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 -1

2. Python 内置工具的效率密码

算法思维决定了"怎么做",而 Python 内置数据结构的特性决定了"做得快不快"。很多 LeetCode 超时(TLE),不是算法错了,而是数据结构用错了。

2.1 时间复杂度速查

数据结构 操作 复杂度 备注
list 索引、尾部 append/pop O(1) 放心用
list 头部 insert/pop、in 查找 O(n) ⚠️ 慎用
dict/set 增删查 O(1) 均摊 LeetCode 首选
deque 头尾 append/pop O(1) BFS 必备
str 拼接(+) O(n) 循环中拼接是 O(n²)

2.2 五个必须避开的性能陷阱

陷阱 1:在 list 头部频繁增删

python 复制代码
# ❌ O(n²)
nums = []
for i in range(10000):
    nums.insert(0, i)

# ✅ O(n)
nums = list(range(9999, -1, -1))

陷阱 2:循环中用 += 拼接字符串

python 复制代码
# ❌ O(n²)
s = ""
for i in range(10000):
    s += str(i)

# ✅ O(n)
s = "".join(str(i) for i in range(10000))

陷阱 3:在 list 中用 in 频繁查找

python 复制代码
# ❌ O(n × m)
if x in big_list:  # 每次都遍历

# ✅ O(m)
big_set = set(big_list)
if x in big_set:   # O(1)

陷阱 4:遍历 list 时直接删除元素

python 复制代码
# ❌ 逻辑错误 + O(n²)
for x in lst:
    if condition(x):
        lst.remove(x)

# ✅ O(n)
lst = [x for x in lst if not condition(x)]

陷阱 5:用 list 做 BFS 队列

python 复制代码
# ❌ list.pop(0) 是 O(n)
queue = []
queue.pop(0)

# ✅ deque.popleft() 是 O(1)
from collections import deque
queue = deque()
queue.popleft()

2.3 五个可以放心用的效率神器

工具 用途 优势
collections.Counter 计数统计 O(n) 构建,O(1) 查询
collections.defaultdict 自动初始化 省去 if key not in dict 判断
collections.deque 双端队列 头尾操作 O(1)
heapq 优先队列 push/pop O(log n)
列表推导式 构造列表 底层 C 实现,比 for+append 快
python 复制代码
from collections import Counter, defaultdict, deque
import heapq

# Counter:一行搞定词频统计
freq = Counter("abracadabra")

# defaultdict:图遍历不再需要判断 key 是否存在
graph = defaultdict(list)
graph['A'].append('B')

# heapq:Top K 问题
nums = [3, 1, 4, 1, 5, 9]
heapq.heapify(nums)           # 原地建堆
smallest = heapq.heappop(nums)

3. 思维与工具的协同:一个完整案例

以 LeetCode 3(无重复字符的最长子串)为例,展示思维方式和 Python 工具如何配合:

python 复制代码
def length_of_longest_substring(s):
    char_set = set()    # 哈希集合:O(1) 查找
    left = max_len = 0
    
    for right in range(len(s)):
        while s[right] in char_set:  # 滑动窗口收缩
            char_set.remove(s[left])
            left += 1
        char_set.add(s[right])       # 窗口扩张
        max_len = max(max_len, right - left + 1)
    
    return max_len

拆解:

  • 算法思维:滑动窗口(双指针的变体)
  • Python 工具 :set 提供 O(1) 的成员检查,避免了 O(n) 的 list 查找
  • 结果:整体 O(n) 时间,O(min(n, charset)) 空间

4. 训练路径建议

  1. 先学套路,再刷题:每种思维方式先理解框架,再集中刷 5-10 道同类题
  2. 刻意练习 Pythonic 写法 :用 set 替代 list 的 in,用 deque 替代 list 做队列,用 Counter 替代手动计数
  3. 五遍刷题法:第一遍硬刚 → 第二遍优化 → 第三遍换思路 → 第四遍限时 → 第五遍融会贯通
  4. 每题三问 :
    • 时间复杂度能不能再降?
    • 有没有更合适的数据结构?
    • 代码能不能更 Pythonic?

写在最后

LeetCode 训练的本质,是训练你的大脑在"问题 → 抽象 → 数据结构 → 算法 → 代码"这条链路上快速运转。Python 给了我们一把锋利的刀,但刀法(算法思维)才是决定胜负的关键。

记住三句话:

  • 查得快,用哈希
  • 头尾操作,用 deque
  • 字符串拼接,用 join

把思维方式内化为直觉,把 Python 工具内化为肌肉记忆,刷题就不再是痛苦,而是一种享受------享受那种"问题被优雅解决"的快感。

我们一起加油,让刀法更加朴实锋利!

相关推荐
YLCHUP4 小时前
【模拟&STL】题解:AT_abc203_e [ABC203E] White Pawn_基础算法_数据结构_STL模板库_AtcoderABC_C++算法竞赛
数据结构·c++·算法·leetcode·动态规划·哈希算法·近邻算法
(●—●)橘子……4 小时前
力扣 第525场周赛 练习理解
数据结构·python·学习·算法·leetcode
wenyq75 小时前
LeetCode 152. Maximum Product Subarray
java·算法·leetcode
文丑颜不良啊6 小时前
LeetCode1181~1200题目解析
java·leetcode
海绵宝宝转agent6 小时前
Leetcode100+面经
java·笔记·学习·算法·leetcode·深度优先
Shaoshing6 小时前
LeetCode100 283.移动零
java·数据结构·算法·leetcode
文丑颜不良啊6 小时前
LeetCode1201~1220题目解析
java·leetcode
文丑颜不良啊6 小时前
LeetCode1161~1180题目解析
java·leetcode
一去不复返的通信er16 小时前
贪心算法-合并区间(LeetCode 56)
算法·leetcode·贪心算法