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}