doocs/leetcode 搜索算法大全:从二分查找到A*算法的完整指南
前言:为什么搜索算法如此重要?
在编程面试和算法竞赛中,搜索算法是最基础也是最核心的技能之一。无论是处理有序数据的二分查找,还是解决图论问题的广度优先搜索(BFS)和深度优先搜索(DFS),亦或是智能寻路的A*算法,掌握这些搜索技术将让你在解决复杂问题时游刃有余。
本文将带你深入探索doocs/leetcode项目中涵盖的各种搜索算法,通过详细的代码示例、算法模板和实战案例,帮助你构建完整的搜索算法知识体系。
📊 搜索算法分类总览
🔍 一、二分查找:有序世界的利器
算法核心思想
二分查找的本质并非"单调性",而是"边界"。只要找到某种性质,使得整个区间一分为二,就可以用二分把边界点找出来。
两种经典模板
模板1:查找左边界
boolean check(int x) {
// 检查条件函数
}
int search(int left, int right) {
while (left < right) {
int mid = (left + right) >> 1;
if (check(mid)) {
right = mid;
} else {
left = mid + 1;
}
}
return left;
}
模板2:查找右边界
boolean check(int x) {
// 检查条件函数
}
int search(int left, int right) {
while (left < right) {
int mid = (left + right + 1) >> 1;
if (check(mid)) {
left = mid;
} else {
right = mid - 1;
}
}
return left;
}
二分查找四步法
- 确定循环条件:
left < right - 计算中间值:先写
mid = (left + right) / 2 - 选择模板:
- 用
right = mid则 else 写left = mid + 1 - 用
left = mid则 else 写right = mid - 1,且 mid 计算要+1
- 用
- 处理结果:循环结束时 left 与 right 相等
实战例题
| 题目 | 难度 | 关键点 |
|---|---|---|
| 在排序数组中查找元素的第一个和最后一个位置 | 中等 | 左右边界二分 |
| x的平方根 | 简单 | 浮点数二分 |
| 寻找峰值 | 中等 | 二分性质判断 |
| 第一个错误的版本 | 简单 | 标准二分应用 |
🚀 二、广度优先搜索(BFS):层层递进的探索
BFS算法框架
from collections import deque
def bfs(start, target):
queue = deque([start])
visited = set([start])
steps = 0
while queue:
size = len(queue)
for _ in range(size):
current = queue.popleft()
if current == target:
return steps
for neighbor in get_neighbors(current):
if neighbor not in visited:
visited.add(neighbor)
queue.append(neighbor)
steps += 1
return -1
BFS变种与应用场景
1. 多源BFS
适用于多个起点同时扩散的场景,如"01矩阵"、"地图中的最高点"等题目。
2. 双向BFS
从起点和终点同时开始搜索,大幅减少搜索空间,适用于"打开转盘锁"、"单词接龙"等题目。
3. 0-1BFS
使用双端队列,边权为0或1时的优化BFS,适用于"网格中的最短路径"等题目。
BFS复杂度分析
| 场景 | 时间复杂度 | 空间复杂度 |
|---|---|---|
| 标准BFS | O(V + E) | O(V) |
| 双向BFS | O(b^(d/2)) | O(b^(d/2)) |
| 多源BFS | O(V + E) | O(V) |
🌊 三、深度优先搜索(DFS):深入探索的艺术
DFS递归模板
def dfs(node, visited, path):
if is_goal(node):
# 处理结果
return
visited.add(node)
for neighbor in get_neighbors(node):
if neighbor not in visited:
path.append(neighbor)
dfs(neighbor, visited, path)
path.pop() # 回溯
visited.remove(node)
DFS迭代模板
def dfs_iterative(start):
stack = [start]
visited = set([start])
while stack:
node = stack.pop()
# 处理当前节点
for neighbor in get_neighbors(node):
if neighbor not in visited:
visited.add(neighbor)
stack.append(neighbor)
DFS高级技巧
1. 回溯算法
用于组合、排列、子集等问题,通过剪枝优化搜索效率。
2. 记忆化搜索
将中间结果缓存,避免重复计算,显著提升性能。
3. 剪枝策略
- 可行性剪枝
- 最优性剪枝
- 重复性剪枝
DFS应用场景
| 问题类型 | 典型题目 | 技巧 |
|---|---|---|
| 组合问题 | 组合总和 | 回溯+剪枝 |
| 排列问题 | 全排列 | 回溯+去重 |
| 子集问题 | 子集 | 回溯 |
| 图遍历 | 岛屿数量 | 递归DFS |
| 路径搜索 | 单词搜索 | 回溯+剪枝 |
⭐ 四、启发式搜索:A*算法
A*算法原理
A*算法结合了Dijkstra算法的最短路径保证和贪心最佳优先搜索的启发式引导,通过评估函数 f(n) = g(n) + h(n) 来选择最优路径。
A*算法实现
import heapq
def a_star(start, target):
open_set = []
heapq.heappush(open_set, (0 + heuristic(start, target), 0, start))
g_score = {start: 0}
came_from = {}
while open_set:
_, current_g, current = heapq.heappop(open_set)
if current == target:
return reconstruct_path(came_from, current)
for neighbor in get_neighbors(current):
tentative_g = current_g + cost(current, neighbor)
if neighbor not in g_score or tentative_g < g_score[neighbor]:
g_score[neighbor] = tentative_g
f_score = tentative_g + heuristic(neighbor, target)
heapq.heappush(open_set, (f_score, tentative_g, neighbor))
came_from[neighbor] = current
return None # 无路径
启发函数设计原则
- 可采纳性:h(n) ≤ h*(n),从不高估实际成本
- 一致性:h(n) ≤ c(n, n') + h(n'),满足三角不等式
- 有效性:计算简单,能有效引导搜索方向
A*算法应用
| 题目 | 启发函数 | 特点 |
|---|---|---|
| 滑动谜题 | 曼哈顿距离 | 拼图游戏 |
| 访问所有节点的最短路径 | 最小生成树代价 | 图遍历优化 |
| 为高尔夫比赛砍树 | 欧几里得距离 | 路径规划 |
🎯 五、搜索算法选择指南
算法选择决策树
性能对比表
| 算法 | 时间复杂度 | 空间复杂度 | 适用场景 |
|---|---|---|---|
| 二分查找 | O(log n) | O(1) | 有序数据查找 |
| BFS | O(V + E) | O(V) | 最短路径、层次遍历 |
| DFS | O(V + E) | O(h) | 路径存在性、回溯问题 |
| A* | O(b^d) | O(b^d) | 启发式路径搜索 |
| 双向BFS | O(b^(d/2)) | O(b^(d/2)) | 已知起点终点的搜索 |
💡 六、实战技巧与优化策略
1. 状态压缩技巧
对于状态空间较大的问题,使用位运算、哈希等方式压缩状态表示。
2. 剪枝优化
- 可行性剪枝:提前终止不可能达到目标的路径
- 最优性剪枝:放弃非最优解路径
- 对称性剪枝:避免重复计算对称状态
3. 记忆化搜索
使用字典或数组缓存中间结果,避免重复计算。
4. 迭代加深搜索
结合DFS的深度限制和BFS的完备性,适用于深度未知的问题。
🚀 七、进阶学习路径
初级 → 中级 → 高级
-
初级阶段:掌握二分查找、基础BFS/DFS
- 练习:二分查找变种、迷宫问题、图遍历
-
中级阶段:学习回溯、记忆化搜索、双向BFS
- 练习:组合问题、最短路径优化、状态压缩
-
高级阶段:掌握A算法、IDA、Meet in Middle
- 练习:启发式搜索、折半搜索、复杂优化
📚 八、推荐练习题目
二分查找系列
-
- 在排序数组中查找元素的第一个和最后一个位置
-
- x的平方根
-
- 寻找峰值
-
- 第一个错误的版本
BFS系列
-
- 岛屿数量
-
- 打开转盘锁
-
- 单词接龙
-
- 二进制矩阵中的最短路径
DFS系列
-
- 单词搜索
-
- 全排列
-
- 子集
-
- 组合总和
A*系列
-
- 滑动谜题
-
- 访问所有节点的最短路径
-
- 为高尔夫比赛砍树
总结
搜索算法是算法领域的基石,从简单的二分查找到复杂的A*算法,每种算法都有其独特的应用场景和优化技巧。通过系统学习和大量练习,你将能够:
- 快速识别问题类型并选择合适的搜索算法
- 熟练运用各种模板解决经典问题
- 掌握优化技巧提升算法效率
- 灵活组合不同算法解决复杂问题
记住,算法学习的关键在于理解思想而非死记模板。多思考、多实践、多总结,你一定能成为搜索算法的高手!
下一步行动:选择2-3个感兴趣的题目,亲自动手实现代码,体会不同搜索算法的精妙之处。祝你编程之路越走越远!
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考



