02-搜索算法
💡 核心结论
二分查找
前提:有序数组
原理:每次排除一半,O(log n)
关键:左右边界、while条件、mid计算
变种:查找第一个/最后一个、查找范围
应用:搜索、求根、最优化问题
深度优先搜索(DFS)
策略:一条路走到底,走不通回退
实现:递归或栈
应用:路径搜索、连通性、拓扑排序
复杂度:O(V + E)
广度优先搜索(BFS)
策略:逐层搜索,找最短路径
实现:队列
应用:最短路径、层序遍历
复杂度:O(V + E)
DFS vs BFS
特性 |
DFS |
BFS |
|---|---|---|
数据结构 |
栈/递归 |
队列 |
空间 |
O(h) |
O(w) |
最短路径 |
❌ |
✅ |
实现 |
更简洁 |
需要队列 |
适用 |
路径、连通性 |
最短路径、层序 |
🔍 二分查找
基本模板
def binary_search(arr, target):
left, right = 0, len(arr) - 1
while left <= right:
mid = left + (right - left) // 2 # 避免溢出
if arr[mid] == target:
return mid
elif arr[mid] < target:
left = mid + 1
else:
right = mid - 1
return -1 # 未找到
左边界查找
def left_bound(arr, target):
left, right = 0, len(arr)
while left < right:
mid = left + (right - left) // 2
if arr[mid] < target:
left = mid + 1
else:
right = mid
return left
右边界查找
def right_bound(arr, target):
left, right = 0, len(arr)
while left < right:
mid = left + (right - left) // 2
if arr[mid] <= target:
left = mid + 1
else:
right = mid
return left - 1
🌲 DFS深度优先
递归实现
def dfs(graph, node, visited):
visited.add(node)
print(node)
for neighbor in graph[node]:
if neighbor not in visited:
dfs(graph, neighbor, visited)
栈实现
def dfs_iterative(graph, start):
visited = set()
stack = [start]
while stack:
node = stack.pop()
if node not in visited:
visited.add(node)
print(node)
for neighbor in reversed(graph[node]):
if neighbor not in visited:
stack.append(neighbor)
🌊 BFS广度优先
队列实现
from collections import deque
def bfs(graph, start):
visited = set([start])
queue = deque([start])
while queue:
node = queue.popleft()
print(node)
for neighbor in graph[node]:
if neighbor not in visited:
visited.add(neighbor)
queue.append(neighbor)
分层BFS
def bfs_levels(graph, start):
visited = set([start])
queue = deque([start])
level = 0
while queue:
size = len(queue)
print(f"Level {level}:")
for _ in range(size):
node = queue.popleft()
print(f" {node}")
for neighbor in graph[node]:
if neighbor not in visited:
visited.add(neighbor)
queue.append(neighbor)
level += 1
📚 LeetCode练习
二分查找
DFS/BFS
💻 完整代码实现
Python 实现
1"""
2二分查找及变种
3"""
4
5def binary_search(arr, target):
6 """基本二分查找"""
7 left, right = 0, len(arr) - 1
8
9 while left <= right:
10 mid = left + (right - left) // 2
11 if arr[mid] == target:
12 return mid
13 elif arr[mid] < target:
14 left = mid + 1
15 else:
16 right = mid - 1
17
18 return -1
19
20
21def left_bound(arr, target):
22 """查找左边界(第一个>=target的位置)"""
23 left, right = 0, len(arr)
24
25 while left < right:
26 mid = left + (right - left) // 2
27 if arr[mid] < target:
28 left = mid + 1
29 else:
30 right = mid
31
32 return left
33
34
35def right_bound(arr, target):
36 """查找右边界(最后一个<=target的位置)"""
37 left, right = 0, len(arr)
38
39 while left < right:
40 mid = left + (right - left) // 2
41 if arr[mid] <= target:
42 left = mid + 1
43 else:
44 right = mid
45
46 return left - 1
47
48
49def search_range(arr, target):
50 """查找target的起始和结束位置"""
51 left = left_bound(arr, target)
52 right = right_bound(arr, target)
53
54 if left >= len(arr) or arr[left] != target:
55 return [-1, -1]
56
57 return [left, right]
58
59
60# ========== 应用 ==========
61
62def my_sqrt(x):
63 """求平方根(整数部分)"""
64 if x < 2:
65 return x
66
67 left, right = 1, x // 2
68
69 while left <= right:
70 mid = left + (right - left) // 2
71 if mid * mid == x:
72 return mid
73 elif mid * mid < x:
74 left = mid + 1
75 else:
76 right = mid - 1
77
78 return right
79
80
81def search_rotated(nums, target):
82 """旋转数组中查找"""
83 left, right = 0, len(nums) - 1
84
85 while left <= right:
86 mid = left + (right - left) // 2
87
88 if nums[mid] == target:
89 return mid
90
91 # 判断哪一半是有序的
92 if nums[left] <= nums[mid]:
93 # 左半有序
94 if nums[left] <= target < nums[mid]:
95 right = mid - 1
96 else:
97 left = mid + 1
98 else:
99 # 右半有序
100 if nums[mid] < target <= nums[right]:
101 left = mid + 1
102 else:
103 right = mid - 1
104
105 return -1
106
107
108def find_peak_element(nums):
109 """找峰值元素"""
110 left, right = 0, len(nums) - 1
111
112 while left < right:
113 mid = left + (right - left) // 2
114 if nums[mid] < nums[mid + 1]:
115 left = mid + 1
116 else:
117 right = mid
118
119 return left
120
121
122def demo():
123 """演示二分查找"""
124 print("=== 二分查找演示 ===\n")
125
126 arr = [1, 2, 2, 2, 3, 4, 5, 5, 6]
127 target = 2
128
129 print(f"数组: {arr}")
130 print(f"目标: {target}\n")
131
132 print(f"基本查找: {binary_search(arr, target)}")
133 print(f"左边界: {left_bound(arr, target)}")
134 print(f"右边界: {right_bound(arr, target)}")
135 print(f"查找范围: {search_range(arr, target)}\n")
136
137 # 平方根
138 x = 8
139 print(f"sqrt({x}) = {my_sqrt(x)}\n")
140
141 # 旋转数组
142 rotated = [4, 5, 6, 7, 0, 1, 2]
143 target = 0
144 print(f"旋转数组: {rotated}")
145 print(f"查找 {target}: {search_rotated(rotated, target)}\n")
146
147 # 峰值
148 nums = [1, 2, 3, 1]
149 print(f"数组: {nums}")
150 print(f"峰值索引: {find_peak_element(nums)}")
151
152
153if __name__ == '__main__':
154 demo()
155
1"""
2DFS和BFS实现
3"""
4
5from collections import deque
6
7# ========== DFS深度优先 ==========
8
9def dfs_recursive(graph, start, visited=None):
10 """DFS递归实现"""
11 if visited is None:
12 visited = set()
13
14 visited.add(start)
15 print(start, end=' ')
16
17 for neighbor in graph[start]:
18 if neighbor not in visited:
19 dfs_recursive(graph, neighbor, visited)
20
21 return visited
22
23
24def dfs_iterative(graph, start):
25 """DFS迭代实现(栈)"""
26 visited = set()
27 stack = [start]
28 result = []
29
30 while stack:
31 node = stack.pop()
32 if node not in visited:
33 visited.add(node)
34 result.append(node)
35
36 # 逆序添加邻居(保证顺序)
37 for neighbor in reversed(graph[node]):
38 if neighbor not in visited:
39 stack.append(neighbor)
40
41 return result
42
43
44# ========== BFS广度优先 ==========
45
46def bfs(graph, start):
47 """BFS实现(队列)"""
48 visited = {start}
49 queue = deque([start])
50 result = []
51
52 while queue:
53 node = queue.popleft()
54 result.append(node)
55
56 for neighbor in graph[node]:
57 if neighbor not in visited:
58 visited.add(neighbor)
59 queue.append(neighbor)
60
61 return result
62
63
64def bfs_shortest_path(graph, start, end):
65 """BFS求最短路径"""
66 if start == end:
67 return [start]
68
69 visited = {start}
70 queue = deque([(start, [start])])
71
72 while queue:
73 node, path = queue.popleft()
74
75 for neighbor in graph[node]:
76 if neighbor not in visited:
77 new_path = path + [neighbor]
78 if neighbor == end:
79 return new_path
80 visited.add(neighbor)
81 queue.append((neighbor, new_path))
82
83 return None
84
85
86def bfs_levels(graph, start):
87 """BFS分层遍历"""
88 visited = {start}
89 queue = deque([start])
90 levels = []
91
92 while queue:
93 level_size = len(queue)
94 current_level = []
95
96 for _ in range(level_size):
97 node = queue.popleft()
98 current_level.append(node)
99
100 for neighbor in graph[node]:
101 if neighbor not in visited:
102 visited.add(neighbor)
103 queue.append(neighbor)
104
105 levels.append(current_level)
106
107 return levels
108
109
110# ========== 应用:岛屿数量 ==========
111
112def num_islands(grid):
113 """岛屿数量(DFS)"""
114 if not grid:
115 return 0
116
117 m, n = len(grid), len(grid[0])
118 count = 0
119
120 def dfs(i, j):
121 if i < 0 or i >= m or j < 0 or j >= n or grid[i][j] == '0':
122 return
123
124 grid[i][j] = '0' # 标记已访问
125
126 # 四个方向
127 dfs(i + 1, j)
128 dfs(i - 1, j)
129 dfs(i, j + 1)
130 dfs(i, j - 1)
131
132 for i in range(m):
133 for j in range(n):
134 if grid[i][j] == '1':
135 count += 1
136 dfs(i, j)
137
138 return count
139
140
141# ========== 应用:课程表(拓扑排序) ==========
142
143def can_finish(num_courses, prerequisites):
144 """判断能否完成所有课程(检测环)"""
145 graph = [[] for _ in range(num_courses)]
146 for course, prereq in prerequisites:
147 graph[prereq].append(course)
148
149 visited = [0] * num_courses # 0:未访问 1:访问中 2:已完成
150
151 def has_cycle(node):
152 if visited[node] == 1:
153 return True # 发现环
154 if visited[node] == 2:
155 return False
156
157 visited[node] = 1
158 for neighbor in graph[node]:
159 if has_cycle(neighbor):
160 return True
161 visited[node] = 2
162 return False
163
164 for i in range(num_courses):
165 if visited[i] == 0:
166 if has_cycle(i):
167 return False
168
169 return True
170
171
172def demo():
173 """演示DFS和BFS"""
174 print("=== DFS和BFS演示 ===\n")
175
176 # 构建图
177 graph = {
178 'A': ['B', 'C'],
179 'B': ['A', 'D', 'E'],
180 'C': ['A', 'F'],
181 'D': ['B'],
182 'E': ['B', 'F'],
183 'F': ['C', 'E']
184 }
185
186 print("图结构:")
187 for node, neighbors in graph.items():
188 print(f" {node}: {neighbors}")
189 print()
190
191 print("DFS遍历(递归): ", end='')
192 dfs_recursive(graph, 'A')
193 print("\n")
194
195 print("DFS遍历(迭代):", dfs_iterative(graph, 'A'))
196 print("BFS遍历:", bfs(graph, 'A'))
197 print()
198
199 # 最短路径
200 path = bfs_shortest_path(graph, 'A', 'F')
201 print(f"从A到F的最短路径: {' -> '.join(path) if path else 'None'}")
202 print()
203
204 # 分层遍历
205 print("BFS分层遍历:")
206 levels = bfs_levels(graph, 'A')
207 for i, level in enumerate(levels):
208 print(f" Level {i}: {level}")
209 print()
210
211 # 岛屿数量
212 grid = [
213 ['1', '1', '0', '0', '0'],
214 ['1', '1', '0', '0', '0'],
215 ['0', '0', '1', '0', '0'],
216 ['0', '0', '0', '1', '1']
217 ]
218 print("岛屿数量:")
219 for row in grid:
220 print(f" {row}")
221 # 注意:会修改grid,这里重新创建
222 grid_copy = [row[:] for row in grid]
223 print(f"岛屿数量: {num_islands(grid_copy)}")
224
225
226if __name__ == '__main__':
227 demo()
228
C++ 实现
1/**
2 * 二分查找及变种实现
3 */
4
5#include <iostream>
6#include <vector>
7#include <algorithm>
8
9using namespace std;
10
11// 基本二分查找
12int binarySearch(const vector<int>& arr, int target) {
13 int left = 0, right = arr.size() - 1;
14
15 while (left <= right) {
16 int mid = left + (right - left) / 2;
17 if (arr[mid] == target) {
18 return mid;
19 } else if (arr[mid] < target) {
20 left = mid + 1;
21 } else {
22 right = mid - 1;
23 }
24 }
25
26 return -1;
27}
28
29// 左边界查找
30int leftBound(const vector<int>& arr, int target) {
31 int left = 0, right = arr.size();
32
33 while (left < right) {
34 int mid = left + (right - left) / 2;
35 if (arr[mid] < target) {
36 left = mid + 1;
37 } else {
38 right = mid;
39 }
40 }
41
42 return left;
43}
44
45// 右边界查找
46int rightBound(const vector<int>& arr, int target) {
47 int left = 0, right = arr.size();
48
49 while (left < right) {
50 int mid = left + (right - left) / 2;
51 if (arr[mid] <= target) {
52 left = mid + 1;
53 } else {
54 right = mid;
55 }
56 }
57
58 return left - 1;
59}
60
61// 平方根
62int mySqrt(int x) {
63 if (x < 2) return x;
64
65 int left = 1, right = x / 2;
66
67 while (left <= right) {
68 int mid = left + (right - left) / 2;
69 long long square = (long long)mid * mid;
70
71 if (square == x) {
72 return mid;
73 } else if (square < x) {
74 left = mid + 1;
75 } else {
76 right = mid - 1;
77 }
78 }
79
80 return right;
81}
82
83// 旋转数组查找
84int searchRotated(const vector<int>& nums, int target) {
85 int left = 0, right = nums.size() - 1;
86
87 while (left <= right) {
88 int mid = left + (right - left) / 2;
89
90 if (nums[mid] == target) {
91 return mid;
92 }
93
94 if (nums[left] <= nums[mid]) {
95 if (nums[left] <= target && target < nums[mid]) {
96 right = mid - 1;
97 } else {
98 left = mid + 1;
99 }
100 } else {
101 if (nums[mid] < target && target <= nums[right]) {
102 left = mid + 1;
103 } else {
104 right = mid - 1;
105 }
106 }
107 }
108
109 return -1;
110}
111
112int main() {
113 cout << "=== 二分查找演示 ===" << endl << endl;
114
115 vector<int> arr = {1, 2, 2, 2, 3, 4, 5, 5, 6};
116 int target = 2;
117
118 cout << "数组: [";
119 for (size_t i = 0; i < arr.size(); i++) {
120 cout << arr[i];
121 if (i < arr.size() - 1) cout << ", ";
122 }
123 cout << "]" << endl;
124 cout << "目标: " << target << endl << endl;
125
126 cout << "基本查找: " << binarySearch(arr, target) << endl;
127 cout << "左边界: " << leftBound(arr, target) << endl;
128 cout << "右边界: " << rightBound(arr, target) << endl << endl;
129
130 int x = 8;
131 cout << "sqrt(" << x << ") = " << mySqrt(x) << endl << endl;
132
133 vector<int> rotated = {4, 5, 6, 7, 0, 1, 2};
134 int search_target = 0;
135 cout << "旋转数组: [";
136 for (size_t i = 0; i < rotated.size(); i++) {
137 cout << rotated[i];
138 if (i < rotated.size() - 1) cout << ", ";
139 }
140 cout << "]" << endl;
141 cout << "查找 " << search_target << ": " << searchRotated(rotated, search_target) << endl;
142
143 return 0;
144}
1/**
2 * DFS和BFS实现
3 */
4
5#include <iostream>
6#include <vector>
7#include <queue>
8#include <stack>
9#include <unordered_set>
10#include <unordered_map>
11
12using namespace std;
13
14// DFS递归
15void dfsRecursive(const unordered_map<int, vector<int>>& graph,
16 int node, unordered_set<int>& visited) {
17 visited.insert(node);
18 cout << node << " ";
19
20 if (graph.count(node)) {
21 for (int neighbor : graph.at(node)) {
22 if (!visited.count(neighbor)) {
23 dfsRecursive(graph, neighbor, visited);
24 }
25 }
26 }
27}
28
29// DFS迭代(栈)
30vector<int> dfsIterative(const unordered_map<int, vector<int>>& graph, int start) {
31 unordered_set<int> visited;
32 stack<int> st;
33 vector<int> result;
34
35 st.push(start);
36
37 while (!st.empty()) {
38 int node = st.top();
39 st.pop();
40
41 if (!visited.count(node)) {
42 visited.insert(node);
43 result.push_back(node);
44
45 if (graph.count(node)) {
46 auto neighbors = graph.at(node);
47 for (auto it = neighbors.rbegin(); it != neighbors.rend(); ++it) {
48 if (!visited.count(*it)) {
49 st.push(*it);
50 }
51 }
52 }
53 }
54 }
55
56 return result;
57}
58
59// BFS(队列)
60vector<int> bfs(const unordered_map<int, vector<int>>& graph, int start) {
61 unordered_set<int> visited;
62 queue<int> q;
63 vector<int> result;
64
65 visited.insert(start);
66 q.push(start);
67
68 while (!q.empty()) {
69 int node = q.front();
70 q.pop();
71 result.push_back(node);
72
73 if (graph.count(node)) {
74 for (int neighbor : graph.at(node)) {
75 if (!visited.count(neighbor)) {
76 visited.insert(neighbor);
77 q.push(neighbor);
78 }
79 }
80 }
81 }
82
83 return result;
84}
85
86// 岛屿数量
87void dfsIsland(vector<vector<char>>& grid, int i, int j) {
88 int m = grid.size();
89 int n = grid[0].size();
90
91 if (i < 0 || i >= m || j < 0 || j >= n || grid[i][j] == '0') {
92 return;
93 }
94
95 grid[i][j] = '0';
96
97 dfsIsland(grid, i + 1, j);
98 dfsIsland(grid, i - 1, j);
99 dfsIsland(grid, i, j + 1);
100 dfsIsland(grid, i, j - 1);
101}
102
103int numIslands(vector<vector<char>>& grid) {
104 if (grid.empty()) return 0;
105
106 int count = 0;
107 int m = grid.size();
108 int n = grid[0].size();
109
110 for (int i = 0; i < m; i++) {
111 for (int j = 0; j < n; j++) {
112 if (grid[i][j] == '1') {
113 count++;
114 dfsIsland(grid, i, j);
115 }
116 }
117 }
118
119 return count;
120}
121
122int main() {
123 cout << "=== DFS和BFS演示 ===" << endl << endl;
124
125 // 构建图
126 unordered_map<int, vector<int>> graph;
127 graph[0] = {1, 2};
128 graph[1] = {0, 3, 4};
129 graph[2] = {0, 5};
130 graph[3] = {1};
131 graph[4] = {1, 5};
132 graph[5] = {2, 4};
133
134 cout << "图结构:" << endl;
135 for (const auto& pair : graph) {
136 cout << " " << pair.first << ": [";
137 for (size_t i = 0; i < pair.second.size(); i++) {
138 cout << pair.second[i];
139 if (i < pair.second.size() - 1) cout << ", ";
140 }
141 cout << "]" << endl;
142 }
143 cout << endl;
144
145 // DFS
146 cout << "DFS遍历(递归): ";
147 unordered_set<int> visited;
148 dfsRecursive(graph, 0, visited);
149 cout << endl << endl;
150
151 cout << "DFS遍历(迭代): ";
152 vector<int> dfs_result = dfsIterative(graph, 0);
153 for (int node : dfs_result) {
154 cout << node << " ";
155 }
156 cout << endl << endl;
157
158 // BFS
159 cout << "BFS遍历: ";
160 vector<int> bfs_result = bfs(graph, 0);
161 for (int node : bfs_result) {
162 cout << node << " ";
163 }
164 cout << endl << endl;
165
166 // 岛屿数量
167 cout << "岛屿数量:" << endl;
168 vector<vector<char>> grid = {
169 {'1', '1', '0', '0', '0'},
170 {'1', '1', '0', '0', '0'},
171 {'0', '0', '1', '0', '0'},
172 {'0', '0', '0', '1', '1'}
173 };
174
175 for (const auto& row : grid) {
176 cout << " ";
177 for (char cell : row) {
178 cout << cell << " ";
179 }
180 cout << endl;
181 }
182 cout << "岛屿数量: " << numIslands(grid) << endl;
183
184 return 0;
185}