图论算法是处理图结构问题的核心工具,广泛应用于路径规划、社交网络分析、计算机网络等领域。以下从基础概念、经典算法及其代码实现展开详细介绍,涵盖 DFS、BFS、最短路径、最小生成树等核心内容,并附 C++ 代码示例及注释。
cpp
#include
#include
#include
using namespace std;
vector> graph; // 邻接表
vector visited; // 访问标记
// 递归版DFS
void dfs_recursive(int u) {
visited[u] = true;
cout << u << " "; // 处理当前节点
for (int v : graph[u]) { // 遍历邻接节点
if (!visited[v]) dfs_recursive(v); // 递归访问未访问节点
}
}
// 迭代版DFS(用栈模拟递归)
void dfs_iterative(int start) {
stack s;
s.push(start);
visited[start] = true;
while (!s.empty()) {
int u = s.top(); s.pop();
cout << u << " ";
// 逆序入栈以保证遍历顺序与递归一致(无向图无需逆序)
for (int i = graph[u].size() - 1; i >= 0; --i) {
int v = graph[u][i];
if (!visited[v]) {
visited[v] = true;
s.push(v);
}
}
}
}
// 测试示例
int main() {
int n = 5; // 顶点0~4
graph.resize(n);
visited.resize(n, false);
// 无向图边连接(0-1, 0-2, 1-3, 2-4)
graph[0] = {1, 2};
graph[1] = {0, 3};
graph[2] = {0, 4};
graph[3] = {1};
graph[4] = {2};
cout << "递归DFS遍历: ";
dfs_recursive(0); // 输出: 0 1 3 2 4
cout << "\n迭代DFS遍历: ";
fill(visited.begin(), visited.end(), false); // 重置标记
dfs_iterative(0); // 输出: 0 2 4 1 3
return 0;
}
cpp
#include
void bfs(int start) {
queue q;
q.push(start);
visited[start] = true;
while (!q.empty()) {
int u = q.front(); q.pop();
cout << u << " ";
for (int v : graph[u]) { // 遍历邻接节点
if (!visited[v]) {
visited[v] = true;
q.push(v); // 子节点入队
}
}
}
}
// 测试示例(沿用DFS的图结构)
cout << "\nBFS遍历: ";
fill(visited.begin(), visited.end(), false); // 重置标记
bfs(0); // 输出: 0 1 2 3 4
cpp
#include
#include
#include
using namespace std;
// 带权图:邻接表存储(顶点,权重)
vector>> weighted_graph;
// dist数组存储起点到各顶点的最短距离
void dijkstra(int start, vector& dist) {
int n = weighted_graph.size();
dist.assign(n, INT_MAX); // 初始化为无穷大
dist[start] = 0;
priority_queue, vector>, greater<>> pq; // 小根堆
pq.push({0, start}); // (当前距离,顶点)
while (!pq.empty()) {
int current_dist, u;
tie(current_dist, u) = pq.top(); // 取出当前最短距离的顶点
pq.pop();
if (current_dist > dist[u]) continue; // 跳过旧距离(已找到更短路径)
for (auto& [v, w] : weighted_graph[u]) { // 遍历邻接边
if (dist[v] > dist[u] + w) { // 松弛操作
dist[v] = dist[u] + w;
pq.push({dist[v], v}); // 新距离入堆
}
}
}
}
// 测试示例
int main() {
int n = 4; // 顶点0~3
weighted_graph.resize(n);
// 边:0→1(2), 0→2(5), 1→2(1), 1→3(3), 2→3(2)
weighted_graph[0] = {{1, 2}, {2, 5}};
weighted_graph[1] = {{2, 1}, {3, 3}};
weighted_graph[2] = {{3, 2}};
vector dist(n);
dijkstra(0, dist); // 起点为0
// 输出各顶点最短距离:0→0(0), 0→1(2), 0→2(3), 0→3(5)
cout << "Dijkstra最短距离: ";
for (int d : dist) cout << d << " "; // 输出: 0 2 3 5
return 0;
}
dp[i][j]
存储顶点 i
到 j
的最短路径。cpp
#include
#include
using namespace std;
void floyd_warshall(vector>& dist) {
int n = dist.size();
for (int k = 0; k < n; ++k) { // 中间顶点
for (int i = 0; i < n; ++i) {
for (int j = 0; j < n; ++j) {
if (dist[i][k] != INT_MAX && dist[k][j] != INT_MAX) {
dist[i][j] = min(dist[i][j], dist[i][k] + dist[k][j]); // 状态转移
}
}
}
}
}
// 测试示例
int main() {
int n = 4;
// 邻接矩阵初始化(INT_MAX表示无直接边)
vector> dist = {
{0, 2, 5, INT_MAX},
{INT_MAX, 0, 1, 3},
{INT_MAX, INT_MAX, 0, 2},
{INT_MAX, INT_MAX, INT_MAX, 0}
};
floyd_warshall(dist);
// 输出顶点0到各顶点的最短距离:0→0(0), 0→1(2), 0→2(3), 0→3(5)
cout << "Floyd-Warshall 0到3的最短距离: " << dist[0][3] << endl; // 输出: 5
return 0;
}
cpp
#include
#include
using namespace std;
// 边结构:(权重,顶点1,顶点2)
struct Edge { int w, u, v; };
// 并查集(Union-Find)
class UnionFind {
private:
vector parent, rank;
public:
UnionFind(int n) : parent(n), rank(n, 1) {
for (int i = 0; i < n; ++i) parent[i] = i;
}
int find(int x) { // 路径压缩
return parent[x] == x ? x : parent[x] = find(parent[x]);
}
bool unite(int x, int y) { // 按秩合并
x = find(x); y = find(y);
if (x == y) return false;
if (rank[x] < rank[y]) swap(x, y);
parent[y] = x;
rank[x] += rank[y];
return true;
}
};
int kruskal(int n, vector& edges) {
sort(edges.begin(), edges.end(), [](Edge a, Edge b) {
return a.w < b.w; // 按权重升序排序
});
UnionFind uf(n);
int mst_weight = 0;
for (Edge e : edges) {
if (uf.unite(e.u, e.v)) { // 若不构成环,加入生成树
mst_weight += e.w;
}
}
return mst_weight; // 最小生成树总权重
}
// 测试示例
int main() {
int n = 4; // 4个顶点
vector edges = {
{2, 0, 1}, {1, 1, 2}, {3, 1, 3}, {5, 0, 2}, {2, 2, 3}
}; // 边权依次为2,1,3,5,2
int total_weight = kruskal(n, edges); // 最小生成树总权重为1+2+2=5
cout << "Kruskal最小生成树权重: " << total_weight << endl; // 输出: 5
return 0;
}
cpp
#include
#include
#include
using namespace std;
int prim(int n, vector>>& graph) {
vector key(n, INT_MAX); // 存储各顶点到生成树的最小边权
vector in_mst(n, false);
priority_queue, vector>, greater<>> pq;
key[0] = 0; // 从顶点0开始
pq.push({0, 0});
int mst_weight = 0;
while (!pq.empty()) {
int u_weight, u;
tie(u_weight, u) = pq.top(); pq.pop();
if (in_mst[u]) continue; // 已加入生成树,跳过
in_mst[u] = true;
mst_weight += u_weight; // 累加边权
for (auto& [v, w] : graph[u]) { // 遍历邻接边
if (!in_mst[v] && w < key[v]) { // 发现更短连接
key[v] = w;
pq.push({w, v});
}
}
}
return mst_weight;
}
// 测试示例(沿用Kruskal的图结构)
int main() {
int n = 4;
vector>> graph(n);
// 边:0-1(2), 0-2(5), 1-2(1), 1-3(3), 2-3(2)
graph[0] = {{1, 2}, {2, 5}};
graph[1] = {{0, 2}, {2, 1}, {3, 3}};
graph[2] = {{0, 5}, {1, 1}, {3, 2}};
graph[3] = {{1, 3}, {2, 2}};
int total_weight = prim(n, graph); // 输出: 5
cout << "Prim最小生成树权重: " << total_weight << endl;
return 0;
}
cpp
#include
#include
using namespace std;
vector topological_sort(vector>& graph, vector& in_degree) {
int n = graph.size();
queue q;
vector order;
// 初始化:入度为0的顶点入队
for (int i = 0; i < n; ++i) {
if (in_degree[i] == 0) q.push(i);
}
while (!q.empty()) {
int u = q.front(); q.pop();
order.push_back(u); // 加入拓扑序
for (int v : graph[u]) { // 减少邻接顶点的入度
if (--in_degree[v] == 0) q.push(v);
}
}
return (order.size() == n) ? order : vector(); // 若含环,返回空
}
// 测试示例(任务调度:0→1, 0→2, 1→3, 2→3)
int main() {
int n = 4;
vector> graph(n);
vector in_degree(n, 0);
// 边:0→1, 0→2, 1→3, 2→3
graph[0] = {1, 2};
graph[1] = {3};
graph[2] = {3};
in_degree[1] = 1; in_degree[2] = 1; in_degree[3] = 2;
vector order = topological_sort(graph, in_degree);
if (order.empty()) cout << "有环!";
else {
cout << "拓扑序: ";
for (int v : order) cout << v << " "; // 输出: 0 1 2 3 或 0 2 1 3
}
return 0;
}
问题类型 | 推荐算法 | 时间复杂度 | 关键条件 |
---|---|---|---|
图遍历 | DFS/BFS | O(V+E) | 无向 / 有向图 |
单源最短路径(非负权) | Dijkstra | O(ElogV) | 边权非负 |
单源最短路径(含负权) | Bellman |