深度优先搜索(DFS)
原理
DFS 是一种图遍历算法,通过沿着某一路径尽可能深地搜索,然后回溯到上一个节点继续搜索未访问的路径,直到所有节点都被访问。
应用场景
- 检测连通性
- 找出所有路径
- 拓扑排序
时间复杂度
- 时间复杂度:O(2^(V + E))
- 空间复杂度:O(V)
模板代码
C++
#include <iostream>
#include <vector>
using namespace std;
void dfs(int u, vector<vector<int>>& g, vector<bool>& v) {
v[u] = true;
for (int n : g[u]) if (!v[n]) dfs(n, g, v);
}
int main() {
vector<vector<int>> g = {{}, {2, 3}, {1, 4}, {1}, {2}};
vector<bool> v(5, false);
dfs(1, g, v);
return 0;
}
Java
import java.util.*;
public class Main {
public static void dfs(int u, List<List<Integer>> g, boolean[] v) {
v[u] = true;
for (int n : g.get(u)) if (!v[n]) dfs(n, g, v);
}
public static void main(String[] args) {
List<List<Integer>> g = Arrays.asList(
Arrays.asList(), Arrays.asList(2, 3), Arrays.asList(1, 4), Arrays.asList(1), Arrays.asList(2)
);
dfs(1, g, new boolean[5]);
}
}
Python
def dfs(u, g, v):
v[u] = True
for n in g[u]:
if not v[n]:
dfs(n, g, v)
g = {1: [2, 3], 2: [1, 4], 3: [1], 4: [2], 5: []}
visited = [False] * 6
dfs(1, g, visited)
广度优先搜索(BFS)
原理
BFS 是一种层次遍历算法,从起始节点开始,依次访问距离为 1、2、3… 的所有节点,直至图中所有节点都被访问。
应用场景
- 最短路径搜索(无权图)
- 寻找连通分量
- 分层遍历问题
时间复杂度
- 时间复杂度:O(V + E)
- 空间复杂度:O(V)
模板代码
C++
#include <iostream>
#include <vector>
#include <queue>
using namespace std;
void bfs(int s, vector<vector<int>>& g, vector<bool>& v) {
queue<int> q; q.push(s); v[s] = true;
while (!q.empty()) {
int u = q.front(); q.pop();
for (int n : g[u]) if (!v[n]) q.push(n), v[n] = true;
}
}
int main() {
vector<vector<int>> g = {{}, {2, 3}, {4}, {1}, {}, {}};
vector<bool> v(6, false);
bfs(1, g, v);
return 0;
}
Java
import java.util.*;
public class Main {
public static void bfs(int s, List<List<Integer>> g, boolean[] v) {
Queue<Integer> q = new LinkedList<>();
q.add(s); v[s] = true;
while (!q.isEmpty()) {
int u = q.poll();
for (int n : g.get(u)) if (!v[n]) q.add(n), v[n] = true;
}
}
public static void main(String[] args) {
List<List<Integer>> g = Arrays.asList(
Arrays.asList(), Arrays.asList(2, 3), Arrays.asList(4), Arrays.asList(1), Arrays.asList(), Arrays.asList()
);
bfs(1, g, new boolean[6]);
}
}
Python
from collections import deque
def bfs(s, g, v):
q = deque([s])
v[s] = True
while q:
u = q.popleft()
for n in g[u]:
if not v[n]:
q.append(n)
v[n] = True
g = {1: [2, 3], 2: [4], 3: [1], 4: [], 5: []}
visited = [False] * 6
bfs(1, g, visited)
Dijkstra 算法
原理
Dijkstra 是一种单源最短路径算法,适用于边权为非负的图。通过一个优先队列,每次扩展当前距离最短的节点,逐步计算到其他节点的最短距离。
应用场景
- 图中带权最短路径问题
时间复杂度
- 时间复杂度:O((V + E) * log V)
- 空间复杂度:O(V)
模板代码
C++
#include <iostream>
#include <vector>
#include <queue>
#include <climits>
using namespace std;
typedef pair<int, int> P;
void dijkstra(int s, vector<vector<P>>& g, vector<int>& d) {
priority_queue<P, vector<P>, greater<P>> pq;
d[s] = 0; pq.push({0, s});
while (!pq.empty()) {
int u = pq.top().second; pq.pop();
for (auto e : g[u]) if (d[u] + e.first < d[e.second]) {
d[e.second] = d[u] + e.first;
pq.push({d[e.second], e.second});
}
}
}
int main() {
vector<vector<P>> g(6);
g[1].push_back({2, 2}); g[1].push_back({4, 3}); g[2].push_back({1, 4});
vector<int> d(6, INT_MAX);
dijkstra(1, g, d);
return 0;
}
Java
import java.util.*;
public class Main {
public static void dijkstra(int s, List<List<int[]>> g, int[] d) {
PriorityQueue<int[]> pq = new PriorityQueue<>(Comparator.comparingInt(a -> a[0]));
d[s] = 0; pq.add(new int[]{0, s});
while (!pq.isEmpty()) {
int u = pq.poll()[1];
for (int[] e : g.get(u)) if (d[u] + e[0] < d[e[1]]) {
d[e[1]] = d[u] + e[0];
pq.add(new int[]{d[e[1]], e[1]});
}
}
}
public static void main(String[] args) {
List<List<int[]>> g = Arrays.asList(
Arrays.asList(), Arrays.asList(new int[]{2, 2}, new int[]{4, 3}), Arrays.asList(new int[]{1, 4}), Arrays.asList(), Arrays.asList(), Arrays.asList()
);
int[] d = new int[6];
Arrays.fill(d, Integer.MAX_VALUE);
dijkstra(1, g, d);
}
}
Python
import heapq
def dijkstra(s, g, d):
d[s] = 0
pq = [(0, s)]
while pq:
u = heapq.heappop(pq)[1]
for w, n in g[u]:
if d[u] + w < d[n]:
d[n] = d[u] + w
heapq.heappush(pq, (d[n], n))
g = {1: [(2, 2), (4, 3)], 2: [(1, 4)], 3: [], 4: [], 5: []}
dist = [float('inf')] * 6
dijkstra(1, g, dist)
质数判断
质数是大于 1 且仅能被 1 和自身整除的数。判断质数是许多算法和数学问题的基础。
原理:
- 枚举从 2 到 n \sqrt{n} n 的所有整数,判断是否能整除 n n n。若发现任何一个整数 d d d,使得 n % d = = 0 n \% d == 0 n%d==0,则 n n n 不是质数。
- 优化点:跳过偶数,只检查 2 和奇数。
应用场景:
- 质因数分解。
时间复杂度:
- O ( n ) O(\sqrt{n}) O(n)
代码模板:
C++:
bool isPrime(int n) {
if (n <= 1) return false;
if (n <= 3) return true;
if (n % 2 == 0 || n % 3 == 0) return false;
for (int i = 5; i * i <= n; i ++ ) {
if (n % i == 0) return false;
}
return true;
}
Java:
public boolean isPrime(int n) {
if (n <= 1) return false;
if (n <= 3) return true;
if (n % 2 == 0 || n % 3 == 0) return false;
for (int i = 5; i * i <= n; i ++ ) {
if (n % i == 0) return false;
}
return true;
}
Python:
def is_prime(n):
if n <= 1:
return False
if n <= 3:
return True
if n % 2 == 0 or n % 3 == 0:
return False
i = 5
while i * i <= n:
if n % i == 0:
return False
i += 6
return True
最大公约数(GCD)
最大公约数是两个整数的所有公约数中最大的一个。
原理:
- 欧几里得算法:利用性质 g c d ( a , b ) = g c d ( b , a % b ) gcd(a, b) = gcd(b, a \% b) gcd(a,b)=gcd(b,a%b) 递归或迭代求解。
应用场景:
- 化简分数。
时间复杂度:
- O ( log min ( a , b ) ) O(\log\min(a, b)) O(logmin(a,b))
代码模板:
C++:
int gcd(int a, int b) {
while (b != 0) {
int temp = a % b;
a = b;
b = temp;
}
return a;
}
Java:
public int gcd(int a, int b) {
while (b != 0) {
int temp = a % b;
a = b;
b = temp;
}
return a;
}
Python:
def gcd(a, b):
while b != 0:
a, b = b, a % b
return a
组合数
组合数 C ( n , k ) C(n, k) C(n,k) 表示从 n n n 个元素中选取 k k k 个的不同组合数。
公式:
C ( n , k ) = n ! k ! ( n − k ) ! C(n, k) = \frac{n!}{k!(n-k)!} C(n,k)=k!(n−k)!n!
优化:
- 使用递推关系 C ( n , k ) = C ( n − 1 , k ) + C ( n − 1 , k − 1 ) C(n, k) = C(n-1, k) + C(n-1, k-1) C(n,k)=C(n−1,k)+C(n−1,k−1)。
- 直接计算公式,避免使用全量阶乘。
应用场景:
- 概率统计。
- 计数问题。
时间复杂度:
- 递推: O ( n × k ) O(n \times k) O(n×k)
- 直接计算: O ( k ) O(k) O(k)
代码模板:
C++:
long long combination(int n, int k) {
if (k > n) return 0;
if (k == 0 || k == n) return 1;
long long res = 1;
for (int i = 1; i <= k; ++i) {
res = res * (n - i + 1) / i;
}
return res;
}
Java:
public long combination(int n, int k) {
if (k > n) return 0;
if (k == 0 || k == n) return 1;
long res = 1;
for (int i = 1; i <= k; ++i) {
res = res * (n - i + 1) / i;
}
return res;
}
Python:
def combination(n, k):
if k > n:
return 0
if k == 0 or k == n:
return 1
res = 1
for i in range(1, k + 1):
res = res * (n - i + 1) // i
return res
线性筛法(埃氏筛优化)
线性筛是一种高效生成质数表的方法,避免了冗余标记。
原理:
- 每个合数只被其最小质因数标记一次。
应用场景:
- 质数表生成。
时间复杂度:
- O ( n ) O(n) O(n)
代码模板:
C++:
vector<int> linearSieve(int n) {
vector<int> primes;
vector<bool> isPrime(n + 1, true);
for (int i = 2; i <= n; ++i) {
if (isPrime[i]) primes.push_back(i);
for (int p : primes) {
if (i * p > n) break;
isPrime[i * p] = false;
if (i % p == 0) break;
}
}
return primes;
}
Java:
public List<Integer> linearSieve(int n) {
List<Integer> primes = new ArrayList<>();
boolean[] isPrime = new boolean[n + 1];
Arrays.fill(isPrime, true);
for (int i = 2; i <= n; ++i) {
if (isPrime[i]) primes.add(i);
for (int p : primes) {
if (i * p > n) break;
isPrime[i * p] = false;
if (i % p == 0) break;
}
}
return primes;
}
Python:
def linear_sieve(n):
primes = []
is_prime = [True] * (n + 1)
for i in range(2, n + 1):
if is_prime[i]:
primes.append(i)
for p in primes:
if i * p > n:
break
is_prime[i * p] = False
if i % p == 0:
break
return primes
快速幂
快速幂是一种高效计算 a b % p a^b \% p ab%p 的方法。
原理:
- 将 b b b 按二进制拆分,利用 a b = ( a b / 2 ) 2 a^{b} = (a^{b/2})^2 ab=(ab/2)2。
应用场景:
- 模运算。
- 大整数幂计算。
时间复杂度:
- O ( log b ) O(\log b) O(logb)
代码模板:
C++:
long long fastPower(long long a, long long b, long long p) {
long long res = 1;
a %= p;
while (b > 0) {
if (b & 1) res = res * a % p;
a = a * a % p;
b >>= 1;
}
return res;
}
Java:
public long fastPower(long a, long b, long p) {
long res = 1;
a %= p;
while (b > 0) {
if ((b & 1) == 1) res = res * a % p;
a = a * a % p;
b >>= 1;
}
return res;
}
Python:
def fast_power(a, b, p):
res = 1
a %= p
while b > 0:
if b & 1:
res = res * a % p
a = a * a % p
b >>= 1
return res