以下是15种经典图算法的完整C++实现,包括详细注释、时间/空间复杂度分析和测试代码。
完整实现代码
/**
* 15种经典图算法完整C++实现
* 编译: g++ -std=c++17 -O2 graph_algorithms.cpp -o graph
*/
#include <iostream>
#include <vector>
#include <queue>
#include <stack>
#include <algorithm>
#include <limits>
#include <functional>
#include <memory>
#include <cmath>
#include <set>
#include <map>
#include <unordered_map>
#include <iomanip>
#include <cstring>
using namespace std;
const int INF = numeric_limits<int>::max();
// ==================== 图数据结构 ====================
class Graph {
public:
int V; // 顶点数
vector<vector<int>> adj; // 邻接表
Graph(int vertices) : V(vertices) {
adj.resize(V);
}
void addEdge(int u, int v) {
adj[u].push_back(v);
}
};
class WeightedGraph {
public:
int V;
vector<vector<pair<int, int>>> adj; // (邻接顶点, 权重)
WeightedGraph(int vertices) : V(vertices) {
adj.resize(V);
}
void addEdge(int u, int v, int w) {
adj[u].push_back({v, w});
}
};
// ==================== 1. 深度优先搜索 (DFS) ====================
/**
* 时间复杂度: O(V + E)
* 空间复杂度: O(V)
* 应用: 拓扑排序、连通分量、检测环
*/
class DFS {
private:
void dfsUtil(int v, vector<bool>& visited, const Graph& g, vector<int>& result) {
visited[v] = true;
result.push_back(v);
for (int neighbor : g.adj[v]) {
if (!visited[neighbor]) {
dfsUtil(neighbor, visited, g, result);
}
}
}
public:
// 递归版本
vector<int> dfsRecursive(const Graph& g, int start) {
vector<bool> visited(g.V, false);
vector<int> result;
dfsUtil(start, visited, g, result);
return result;
}
// 迭代版本
vector<int> dfsIterative(const Graph& g, int start) {
vector<bool> visited(g.V, false);
vector<int> result;
stack<int> stk;
stk.push(start);
while (!stk.empty()) {
int v = stk.top();
stk.pop();
if (!visited[v]) {
visited[v] = true;
result.push_back(v);
// 将邻接点逆序入栈以保持与递归版本相同的顺序
for (auto it = g.adj[v].rbegin(); it != g.adj[v].rend(); ++it) {
if (!visited[*it]) {
stk.push(*it);
}
}
}
}
return result;
}
// 检测无向图是否有环
bool hasCycle(const Graph& g) {
vector<bool> visited(g.V, false);
for (int i = 0; i < g.V; i++) {
if (!visited[i]) {
if (hasCycleUtil(i, -1, visited, g)) {
return true;
}
}
}
return false;
}
private:
bool hasCycleUtil(int v, int parent, vector<bool>& visited, const Graph& g) {
visited[v] = true;
for (int neighbor : g.adj[v]) {
if (!visited[neighbor]) {
if (hasCycleUtil(neighbor, v, visited, g)) {
return true;
}
} else if (neighbor != parent) {
return true;
}
}
return false;
}
};
// ==================== 2. 广度优先搜索 (BFS) ====================
/**
* 时间复杂度: O(V + E)
* 空间复杂度: O(V)
* 应用: 最短路径(无权图)、层级遍历
*/
class BFS {
public:
// 基本BFS
vector<int> bfs(const Graph& g, int start) {
vector<bool> visited(g.V, false);
vector<int> result;
queue<int> q;
visited[start] = true;
q.push(start);
while (!q.empty()) {
int v = q.front();
q.pop();
result.push_back(v);
for (int neighbor : g.adj[v]) {
if (!visited[neighbor]) {
visited[neighbor] = true;
q.push(neighbor);
}
}
}
return result;
}
// 计算无权图的最短路径距离
vector<int> shortestPathUnweighted(const Graph& g, int start) {
vector<int> distance(g.V, -1);
queue<int> q;
distance[start] = 0;
q.push(start);
while (!q.empty()) {
int v = q.front();
q.pop();
for (int neighbor : g.adj[v]) {
if (distance[neighbor] == -1) {
distance[neighbor] = distance[v] + 1;
q.push(neighbor);
}
}
}
return distance;
}
// 获取BFS路径
vector<int> getBFSPath(const Graph& g, int start, int end) {
vector<bool> visited(g.V, false);
vector<int> parent(g.V, -1);
queue<int> q;
visited[start] = true;
q.push(start);
while (!q.empty()) {
int v = q.front();
q.pop();
if (v == end) break;
for (int neighbor : g.adj[v]) {
if (!visited[neighbor]) {
visited[neighbor] = true;
parent[neighbor] = v;
q.push(neighbor);
}
}
}
// 重构路径
vector<int> path;
for (int v = end; v != -1; v = parent[v]) {
path.push_back(v);
}
reverse(path.begin(), path.end());
if (path[0] != start) return {}; // 无路径
return path;
}
};
// ==================== 3. 拓扑排序 ====================
/**
* 时间复杂度: O(V + E)
* 空间复杂度: O(V)
* 应用: 课程安排、任务调度
*/
class TopologicalSort {
private:
bool dfs(int v, vector<bool>& visited, vector<bool>& recStack,
const Graph& g, vector<int>& result) {
visited[v] = true;
recStack[v] = true;
for (int neighbor : g.adj[v]) {
if (!visited[neighbor]) {
if (dfs(neighbor, visited, recStack, g, result)) {
return true;
}
} else if (recStack[neighbor]) {
return true; // 检测到环
}
}
recStack[v] = false;
result.push_back(v);
return false;
}
public:
// Kahn算法 (基于BFS)
vector<int> kahn(const Graph& g) {
vector<int> inDegree(g.V, 0);
vector<int> result;
// 计算入度
for (int u = 0; u < g.V; u++) {
for (int v : g.adj[u]) {
inDegree[v]++;
}
}
// 入度为0的顶点入队
queue<int> q;
for (int i = 0; i < g.V; i++) {
if (inDegree[i] == 0) {
q.push(i);
}
}
int count = 0;
while (!q.empty()) {
int u = q.front();
q.pop();
result.push_back(u);
for (int v : g.adj[u]) {
if (--inDegree[v] == 0) {
q.push(v);
}
}
count++;
}
if (count != g.V) {
cout << "图中有环,无法进行拓扑排序" << endl;
return {};
}
return result;
}
// DFS算法
vector<int> dfsSort(const Graph& g) {
vector<bool> visited(g.V, false);
vector<bool> recStack(g.V, false);
vector<int> result;
for (int i = 0; i < g.V; i++) {
if (!visited[i]) {
if (dfs(i, visited, recStack, g, result)) {
cout << "图中有环" << endl;
return {};
}
}
}
reverse(result.begin(), result.end());
return result;
}
// 获取所有可能的拓扑排序
void allTopologicalSorts(const Graph& g) {
vector<bool> visited(g.V, false);
vector<int> inDegree(g.V, 0);
vector<int> result;
// 计算入度
for (int u = 0; u < g.V; u++) {
for (int v : g.adj[u]) {
inDegree[v]++;
}
}
allTopologicalSortsUtil(g, visited, inDegree, result);
}
private:
void allTopologicalSortsUtil(const Graph& g, vector<bool>& visited,
vector<int>& inDegree, vector<int>& result) {
bool flag = false;
for (int i = 0; i < g.V; i++) {
if (!visited[i] && inDegree[i] == 0) {
visited[i] = true;
result.push_back(i);
for (int neighbor : g.adj[i]) {
inDegree[neighbor]--;
}
allTopologicalSortsUtil(g, visited, inDegree, result);
// 回溯
visited[i] = false;
result.pop_back();
for (int neighbor : g.adj[i]) {
inDegree[neighbor]++;
}
flag = true;
}
}
if (!flag) {
for (int v : result) {
cout << v << " ";
}
cout << endl;
}
}
};
// ==================== 4. Dijkstra算法 ====================
/**
* 时间复杂度: O((V+E)logV) 使用优先队列
* 空间复杂度: O(V)
* 应用: 单源最短路径(非负权)
*/
class Dijkstra {
public:
vector<int> dijkstra(const WeightedGraph& g, int src) {
vector<int> dist(g.V, INF);
vector<bool> visited(g.V, false);
// 优先队列存储(距离, 顶点)
using pii = pair<int, int>;
priority_queue<pii, vector<pii>, greater<pii>> pq;
dist[src] = 0;
pq.push({0, src});
while (!pq.empty()) {
int u = pq.top().second;
pq.pop();
if (visited[u]) continue;
visited[u] = true;
for (const auto& edge : g.adj[u]) {
int v = edge.first;
int weight = edge.second;
if (!visited[v] && dist[u] + weight < dist[v]) {
dist[v] = dist[u] + weight;
pq.push({dist[v], v});
}
}
}
return dist;
}
// 获取最短路径
vector<int> getShortestPath(const WeightedGraph& g, int src, int dest) {
vector<int> dist(g.V, INF);
vector<int> parent(g.V, -1);
using pii = pair<int, int>;
priority_queue<pii, vector<pii>, greater<pii>> pq;
dist[src] = 0;
pq.push({0, src});
while (!pq.empty()) {
int u = pq.top().second;
pq.pop();
if (u == dest) break;
for (const auto& edge : g.adj[u]) {
int v = edge.first;
int weight = edge.second;
if (dist[u] + weight < dist[v]) {
dist[v] = dist[u] + weight;
parent[v] = u;
pq.push({dist[v], v});
}
}
}
// 重构路径
vector<int> path;
if (dist[dest] == INF) return path;
for (int v = dest; v != -1; v = parent[v]) {
path.push_back(v);
}
reverse(path.begin(), path.end());
return path;
}
};
// ==================== 5. Bellman-Ford算法 ====================
/**
* 时间复杂度: O(VE)
* 空间复杂度: O(V)
* 应用: 单源最短路径(可负权)、检测负权环
*/
class BellmanFord {
public:
struct Edge {
int u, v, w;
Edge(int u, int v, int w) : u(u), v(v), w(w) {}
};
vector<int> bellmanFord(int V, const vector<Edge>& edges, int src) {
vector<int> dist(V, INF);
dist[src] = 0;
// 松弛V-1次
for (int i = 1; i <= V - 1; i++) {
for (const auto& edge : edges) {
int u = edge.u;
int v = edge.v;
int w = edge.w;
if (dist[u] != INF && dist[u] + w < dist[v]) {
dist[v] = dist[u] + w;
}
}
}
// 检测负权环
for (const auto& edge : edges) {
int u = edge.u;
int v = edge.v;
int w = edge.w;
if (dist[u] != INF && dist[u] + w < dist[v]) {
cout << "图中存在负权环!" << endl;
return {};
}
}
return dist;
}
// 检测负权环
bool hasNegativeCycle(int V, const vector<Edge>& edges) {
vector<int> dist(V, 0);
// 额外松弛一次检测环
for (int i = 0; i < V; i++) {
for (const auto& edge : edges) {
int u = edge.u;
int v = edge.v;
int w = edge.w;
if (dist[u] + w < dist[v]) {
if (i == V - 1) {
return true;
}
dist[v] = dist[u] + w;
}
}
}
return false;
}
};
// ==================== 6. Floyd-Warshall算法 ====================
/**
* 时间复杂度: O(V³)
* 空间复杂度: O(V²)
* 应用: 所有顶点对最短路径
*/
class FloydWarshall {
public:
vector<vector<int>> floydWarshall(const vector<vector<int>>& graph) {
int V = graph.size();
vector<vector<int>> dist = graph;
// 初始化
for (int i = 0; i < V; i++) {
for (int j = 0; j < V; j++) {
if (i == j) dist[i][j] = 0;
else if (graph[i][j] == 0) dist[i][j] = INF;
}
}
// 动态规划
for (int k = 0; k < V; k++) {
for (int i = 0; i < V; i++) {
for (int j = 0; j < V; j++) {
if (dist[i][k] != INF && dist[k][j] != INF) {
dist[i][j] = min(dist[i][j], dist[i][k] + dist[k][j]);
}
}
}
}
return dist;
}
// 检测负权环
bool hasNegativeCycle(const vector<vector<int>>& dist) {
int V = dist.size();
for (int i = 0; i < V; i++) {
if (dist[i][i] < 0) {
return true;
}
}
return false;
}
// 重建路径
vector<int> getPath(const vector<vector<int>>& next, int u, int v) {
if (next[u][v] == -1) return {};
vector<int> path = {u};
while (u != v) {
u = next[u][v];
path.push_back(u);
}
return path;
}
};
// ==================== 7. Prim算法 ====================
/**
* 时间复杂度: O((V+E)logV)
* 空间复杂度: O(V)
* 应用: 最小生成树(稠密图)
*/
class Prim {
public:
int primMST(const WeightedGraph& g) {
vector<int> key(g.V, INF);
vector<bool> inMST(g.V, false);
vector<int> parent(g.V, -1);
using pii = pair<int, int>;
priority_queue<pii, vector<pii>, greater<pii>> pq;
int src = 0;
key[src] = 0;
pq.push({0, src});
int mstWeight = 0;
while (!pq.empty()) {
int u = pq.top().second;
pq.pop();
if (inMST[u]) continue;
inMST[u] = true;
mstWeight += key[u];
for (const auto& edge : g.adj[u]) {
int v = edge.first;
int weight = edge.second;
if (!inMST[v] && weight < key[v]) {
key[v] = weight;
parent[v] = u;
pq.push({key[v], v});
}
}
}
// 打印MST
cout << "Prim MST 边:" << endl;
for (int i = 1; i < g.V; i++) {
cout << parent[i] << " - " << i << endl;
}
return mstWeight;
}
};
// ==================== 8. Kruskal算法 ====================
/**
* 时间复杂度: O(ElogE) 或 O(ElogV)
* 空间复杂度: O(V)
* 应用: 最小生成树(稀疏图)
*/
class Kruskal {
private:
struct Edge {
int u, v, weight;
Edge(int u, int v, int w) : u(u), v(v), weight(w) {}
bool operator<(const Edge& other) const {
return weight < other.weight;
}
};
class UnionFind {
private:
vector<int> parent, rank;
public:
UnionFind(int n) {
parent.resize(n);
rank.resize(n, 0);
for (int i = 0; i < n; i++) {
parent[i] = i;
}
}
int find(int x) {
if (parent[x] != x) {
parent[x] = find(parent[x]);
}
return parent[x];
}
void unite(int x, int y) {
int rootX = find(x);
int rootY = find(y);
if (rootX == rootY) return;
if (rank[rootX] < rank[rootY]) {
parent[rootX] = rootY;
} else if (rank[rootX] > rank[rootY]) {
parent[rootY] = rootX;
} else {
parent[rootY] = rootX;
rank[rootX]++;
}
}
bool connected(int x, int y) {
return find(x) == find(y);
}
};
public:
int kruskalMST(int V, vector<Edge>& edges) {
sort(edges.begin(), edges.end());
UnionFind uf(V);
int mstWeight = 0;
cout << "Kruskal MST 边:" << endl;
for (const auto& edge : edges) {
if (!uf.connected(edge.u, edge.v)) {
uf.unite(edge.u, edge.v);
mstWeight += edge.weight;
cout << edge.u << " - " << edge.v << " : " << edge.weight << endl;
}
}
return mstWeight;
}
};
// ==================== 9. 强连通分量 (Kosaraju算法) ====================
/**
* 时间复杂度: O(V + E)
* 空间复杂度: O(V)
* 应用: 有向图的强连通分量
*/
class Kosaraju {
private:
void dfsFirst(int v, vector<bool>& visited, stack<int>& stk, const Graph& g) {
visited[v] = true;
for (int neighbor : g.adj[v]) {
if (!visited[neighbor]) {
dfsFirst(neighbor, visited, stk, g);
}
}
stk.push(v);
}
void dfsSecond(int v, vector<bool>& visited, const Graph& g, vector<int>& comp) {
visited[v] = true;
comp.push_back(v);
for (int neighbor : g.adj[v]) {
if (!visited[neighbor]) {
dfsSecond(neighbor, visited, g, comp);
}
}
}
Graph getTranspose(const Graph& g) {
Graph gt(g.V);
for (int u = 0; u < g.V; u++) {
for (int v : g.adj[u]) {
gt.addEdge(v, u);
}
}
return gt;
}
public:
vector<vector<int>> kosaraju(const Graph& g) {
vector<bool> visited(g.V, false);
stack<int> stk;
// 第一次DFS
for (int i = 0; i < g.V; i++) {
if (!visited[i]) {
dfsFirst(i, visited, stk, g);
}
}
// 获取转置图
Graph gt = getTranspose(g);
// 第二次DFS
fill(visited.begin(), visited.end(), false);
vector<vector<int>> sccs;
while (!stk.empty()) {
int v = stk.top();
stk.pop();
if (!visited[v]) {
vector<int> comp;
dfsSecond(v, visited, gt, comp);
sccs.push_back(comp);
}
}
return sccs;
}
};
// ==================== 10. Tarjan算法 ====================
/**
* 时间复杂度: O(V + E)
* 空间复杂度: O(V)
* 应用: 强连通分量、桥、割点
*/
class Tarjan {
private:
int time;
vector<int> disc; // 发现时间
vector<int> low; // 最早可到达的祖先
vector<bool> inStack;
stack<int> stk;
void dfs(int u, const Graph& g, vector<vector<int>>& sccs) {
disc[u] = low[u] = ++time;
stk.push(u);
inStack[u] = true;
for (int v : g.adj[u]) {
if (disc[v] == -1) {
dfs(v, g, sccs);
low[u] = min(low[u], low[v]);
} else if (inStack[v]) {
low[u] = min(low[u], disc[v]);
}
}
// 如果u是强连通分量的根
if (low[u] == disc[u]) {
vector<int> comp;
while (stk.top() != u) {
int v = stk.top();
stk.pop();
inStack[v] = false;
comp.push_back(v);
}
int v = stk.top();
stk.pop();
inStack[v] = false;
comp.push_back(v);
sccs.push_back(comp);
}
}
public:
vector<vector<int>> tarjanSCC(const Graph& g) {
time = 0;
disc.assign(g.V, -1);
low.assign(g.V, -1);
inStack.assign(g.V, false);
vector<vector<int>> sccs;
for (int i = 0; i < g.V; i++) {
if (disc[i] == -1) {
dfs(i, g, sccs);
}
}
return sccs;
}
// 查找无向图的桥
vector<pair<int, int>> findBridges(const Graph& g) {
time = 0;
disc.assign(g.V, -1);
low.assign(g.V, -1);
vector<pair<int, int>> bridges;
dfsBridges(0, -1, g, bridges);
return bridges;
}
private:
void dfsBridges(int u, int parent, const Graph& g, vector<pair<int, int>>& bridges) {
disc[u] = low[u] = ++time;
for (int v : g.adj[u]) {
if (v == parent) continue;
if (disc[v] == -1) {
dfsBridges(v, u, g, bridges);
low[u] = min(low[u], low[v]);
if (low[v] > disc[u]) {
bridges.push_back({u, v});
}
} else {
low[u] = min(low[u], disc[v]);
}
}
}
};
// ==================== 11. Ford-Fulkerson最大流 ====================
/**
* 时间复杂度: O(E * max_flow)
* 空间复杂度: O(V)
* 应用: 网络流、最大匹配
*/
class FordFulkerson {
private:
bool bfs(int s, int t, const vector<vector<int>>& capacity,
vector<int>& parent, const vector<vector<int>>& adj) {
int V = adj.size();
vector<bool> visited(V, false);
queue<int> q;
q.push(s);
visited[s] = true;
parent[s] = -1;
while (!q.empty()) {
int u = q.front();
q.pop();
for (int v : adj[u]) {
if (!visited[v] && capacity[u][v] > 0) {
if (v == t) {
parent[v] = u;
return true;
}
q.push(v);
visited[v] = true;
parent[v] = u;
}
}
}
return false;
}
public:
int maxFlow(int s, int t, vector<vector<int>>& capacity,
const vector<vector<int>>& adj) {
int V = adj.size();
vector<vector<int>> residual = capacity;
vector<int> parent(V);
int maxFlow = 0;
while (bfs(s, t, residual, parent, adj)) {
int pathFlow = INF;
// 找到增广路径的最小容量
for (int v = t; v != s; v = parent[v]) {
int u = parent[v];
pathFlow = min(pathFlow, residual[u][v]);
}
// 更新残留网络
for (int v = t; v != s; v = parent[v]) {
int u = parent[v];
residual[u][v] -= pathFlow;
residual[v][u] += pathFlow;
}
maxFlow += pathFlow;
}
return maxFlow;
}
};
// ==================== 12. Edmonds-Karp算法 ====================
/**
* 时间复杂度: O(VE²)
* 空间复杂度: O(V²)
* 应用: 最大流(BFS实现)
*/
class EdmondsKarp : public FordFulkerson {
// 使用父类的实现,Edmonds-Karp是Ford-Fulkerson的特殊情况
// 使用BFS寻找增广路径
};
// ==================== 13. Dinic算法 ====================
/**
* 时间复杂度: O(V²E)
* 空间复杂度: O(V+E)
* 应用: 最大流(分层图)
*/
class Dinic {
private:
bool bfs(int s, int t, const vector<vector<int>>& level,
const vector<vector<int>>& capacity, vector<int>& dist) {
int V = level.size();
fill(dist.begin(), dist.end(), -1);
queue<int> q;
dist[s] = 0;
q.push(s);
while (!q.empty()) {
int u = q.front();
q.pop();
for (int v = 0; v < V; v++) {
if (dist[v] == -1 && capacity[u][v] > 0) {
dist[v] = dist[u] + 1;
q.push(v);
}
}
}
return dist[t] != -1;
}
int dfs(int u, int t, int flow, const vector<vector<int>>& capacity,
vector<vector<int>>& residual, const vector<int>& dist) {
if (u == t) return flow;
for (int v = 0; v < capacity.size(); v++) {
if (dist[v] == dist[u] + 1 && capacity[u][v] > 0) {
int currentFlow = min(flow, capacity[u][v]);
int minPathFlow = dfs(v, t, currentFlow, capacity, residual, dist);
if (minPathFlow > 0) {
capacity[u][v] -= minPathFlow;
capacity[v][u] += minPathFlow;
return minPathFlow;
}
}
}
return 0;
}
public:
int maxFlow(int s, int t, vector<vector<int>>& capacity) {
int V = capacity.size();
vector<int> dist(V);
int maxFlow = 0;
while (bfs(s, t, capacity, capacity, dist)) {
while (int flow = dfs(s, t, INF, capacity, capacity, dist)) {
maxFlow += flow;
}
}
return maxFlow;
}
};
// ==================== 14. 匈牙利算法 ====================
/**
* 时间复杂度: O(VE)
* 空间复杂度: O(V)
* 应用: 二分图最大匹配
*/
class Hungarian {
private:
bool bpm(int u, const vector<vector<int>>& bpGraph,
vector<bool>& seen, vector<int>& matchR) {
int N = bpGraph[0].size();
for (int v = 0; v < N; v++) {
if (bpGraph[u][v] && !seen[v]) {
seen[v] = true;
if (matchR[v] < 0 || bpm(matchR[v], bpGraph, seen, matchR)) {
matchR[v] = u;
return true;
}
}
}
return false;
}
public:
vector<pair<int, int>> maxBipartiteMatching(const vector<vector<int>>& bpGraph) {
int M = bpGraph.size();
int N = bpGraph[0].size();
vector<int> matchR(N, -1);
int result = 0;
for (int u = 0; u < M; u++) {
vector<bool> seen(N, false);
if (bpm(u, bpGraph, seen, matchR)) {
result++;
}
}
vector<pair<int, int>> matches;
for (int v = 0; v < N; v++) {
if (matchR[v] != -1) {
matches.push_back({matchR[v], v});
}
}
cout << "最大匹配数: " << result << endl;
return matches;
}
};
// ==================== 15. A*搜索算法 ====================
/**
* 时间复杂度: O(b^d) 其中b是分支因子,d是深度
* 空间复杂度: O(b^d)
* 应用: 启发式搜索、路径规划
*/
class AStar {
private:
struct Node {
int id;
int f, g, h;
Node* parent;
Node(int id) : id(id), f(0), g(0), h(0), parent(nullptr) {}
bool operator>(const Node& other) const {
return f > other.f;
}
};
int heuristic(int a, int b, const vector<pair<int, int>>& coordinates) {
// 曼哈顿距离
return abs(coordinates[a].first - coordinates[b].first) +
abs(coordinates[a].second - coordinates[b].second);
}
public:
vector<int> aStar(int start, int target, const WeightedGraph& g,
const vector<pair<int, int>>& coordinates) {
int V = g.V;
vector<Node*> nodes(V, nullptr);
vector<bool> closed(V, false);
auto cmp = [](Node* a, Node* b) { return a->f > b->f; };
priority_queue<Node*, vector<Node*>, decltype(cmp)> open(cmp);
Node* startNode = new Node(start);
startNode->g = 0;
startNode->h = heuristic(start, target, coordinates);
startNode->f = startNode->g + startNode->h;
nodes[start] = startNode;
open.push(startNode);
while (!open.empty()) {
Node* current = open.top();
open.pop();
if (current->id == target) {
// 重构路径
vector<int> path;
for (Node* node = current; node != nullptr; node = node->parent) {
path.push_back(node->id);
}
reverse(path.begin(), path.end());
// 清理内存
for (Node* node : nodes) {
if (node) delete node;
}
return path;
}
closed[current->id] = true;
for (const auto& edge : g.adj[current->id]) {
int neighbor = edge.first;
int weight = edge.second;
if (closed[neighbor]) continue;
int gScore = current->g + weight;
if (!nodes[neighbor]) {
nodes[neighbor] = new Node(neighbor);
}
if (gScore < nodes[neighbor]->g || nodes[neighbor]->g == 0) {
nodes[neighbor]->parent = current;
nodes[neighbor]->g = gScore;
nodes[neighbor]->h = heuristic(neighbor, target, coordinates);
nodes[neighbor]->f = nodes[neighbor]->g + nodes[neighbor]->h;
// 需要重新插入优先队列
priority_queue<Node*, vector<Node*>, decltype(cmp)> temp(cmp);
while (!open.empty()) {
temp.push(open.top());
open.pop();
}
temp.push(nodes[neighbor]);
open = move(temp);
}
}
}
// 清理内存
for (Node* node : nodes) {
if (node) delete node;
}
return {}; // 无路径
}
};
// ==================== 测试函数 ====================
void testGraphAlgorithms() {
cout << "=== 图算法测试 ===" << endl;
// 创建测试图
Graph g(6);
g.addEdge(0, 1);
g.addEdge(0, 2);
g.addEdge(1, 3);
g.addEdge(2, 3);
g.addEdge(2, 4);
g.addEdge(3, 5);
g.addEdge(4, 5);
cout << "\n1. DFS测试:" << endl;
DFS dfs;
vector<int> dfsResult = dfs.dfsRecursive(g, 0);
cout << "DFS遍历: ";
for (int v : dfsResult) cout << v << " ";
cout << endl;
cout << "\n2. BFS测试:" << endl;
BFS bfs;
vector<int> bfsResult = bfs.bfs(g, 0);
cout << "BFS遍历: ";
for (int v : bfsResult) cout << v << " ";
cout << endl;
cout << "\n3. 拓扑排序测试:" << endl;
TopologicalSort topo;
vector<int> topoResult = topo.kahn(g);
cout << "拓扑排序: ";
for (int v : topoResult) cout << v << " ";
cout << endl;
cout << "\n4. Dijkstra算法测试:" << endl;
WeightedGraph wg(5);
wg.addEdge(0, 1, 4);
wg.addEdge(0, 2, 1);
wg.addEdge(2, 1, 2);
wg.addEdge(1, 3, 1);
wg.addEdge(2, 3, 5);
wg.addEdge(3, 4, 3);
Dijkstra dijkstra;
vector<int> dist = dijkstra.dijkstra(wg, 0);
cout << "从0到各顶点的最短距离: ";
for (int i = 0; i < dist.size(); i++) {
cout << "0->" << i << "=";
if (dist[i] == INF) cout << "INF ";
else cout << dist[i] << " ";
}
cout << endl;
cout << "\n5. Bellman-Ford算法测试:" << endl;
BellmanFord bf;
vector<BellmanFord::Edge> edges = {
{0, 1, 4}, {0, 2, 1}, {2, 1, 2},
{1, 3, 1}, {2, 3, 5}, {3, 4, 3}
};
vector<int> bfDist = bf.bellmanFord(5, edges, 0);
cout << "Bellman-Ford距离: ";
for (int d : bfDist) cout << d << " ";
cout << endl;
cout << "\n6. Floyd-Warshall算法测试:" << endl;
vector<vector<int>> graph = {
{0, 4, 1, 0, 0},
{0, 0, 0, 1, 0},
{0, 2, 0, 5, 0},
{0, 0, 0, 0, 3},
{0, 0, 0, 0, 0}
};
FloydWarshall fw;
vector<vector<int>> fwDist = fw.floydWarshall(graph);
cout << "所有顶点对最短距离:" << endl;
for (int i = 0; i < fwDist.size(); i++) {
for (int j = 0; j < fwDist[i].size(); j++) {
if (fwDist[i][j] == INF) cout << "INF ";
else cout << fwDist[i][j] << " ";
}
cout << endl;
}
cout << "\n7. Prim算法测试:" << endl;
WeightedGraph mstGraph(5);
mstGraph.addEdge(0, 1, 2);
mstGraph.addEdge(0, 3, 6);
mstGraph.addEdge(1, 0, 2);
mstGraph.addEdge(1, 2, 3);
mstGraph.addEdge(1, 3, 8);
mstGraph.addEdge(1, 4, 5);
mstGraph.addEdge(2, 1, 3);
mstGraph.addEdge(2, 4, 7);
mstGraph.addEdge(3, 0, 6);
mstGraph.addEdge(3, 1, 8);
mstGraph.addEdge(3, 4, 9);
mstGraph.addEdge(4, 1, 5);
mstGraph.addEdge(4, 2, 7);
mstGraph.addEdge(4, 3, 9);
Prim prim;
int primWeight = prim.primMST(mstGraph);
cout << "Prim MST总权重: " << primWeight << endl;
cout << "\n8. Kruskal算法测试:" << endl;
vector<Kruskal::Edge> kruskalEdges = {
{0, 1, 2}, {0, 3, 6}, {1, 2, 3},
{1, 3, 8}, {1, 4, 5}, {2, 4, 7},
{3, 4, 9}
};
Kruskal kruskal;
int kruskalWeight = kruskal.kruskalMST(5, kruskalEdges);
cout << "Kruskal MST总权重: " << kruskalWeight << endl;
cout << "\n9. Kosaraju算法测试:" << endl;
Graph sccGraph(5);
sccGraph.addEdge(0, 2);
sccGraph.addEdge(2, 1);
sccGraph.addEdge(1, 0);
sccGraph.addEdge(0, 3);
sccGraph.addEdge(3, 4);
Kosaraju kosaraju;
vector<vector<int>> sccs = kosaraju.kosaraju(sccGraph);
cout << "强连通分量:" << endl;
for (int i = 0; i < sccs.size(); i++) {
cout << "分量" << i << ": ";
for (int v : sccs[i]) cout << v << " ";
cout << endl;
}
cout << "\n10. Tarjan算法测试:" << endl;
Tarjan tarjan;
vector<vector<int>> tarjanSCCs = tarjan.tarjanSCC(sccGraph);
cout << "Tarjan算法找到的SCC:" << endl;
for (int i = 0; i < tarjanSCCs.size(); i++) {
cout << "分量" << i << ": ";
for (int v : tarjanSCCs[i]) cout << v << " ";
cout << endl;
}
cout << "\n11. Ford-Fulkerson最大流测试:" << endl;
int V = 6;
vector<vector<int>> capacity(V, vector<int>(V, 0));
vector<vector<int>> adj(V);
// 添加边
auto addFlowEdge = [&](int u, int v, int w) {
capacity[u][v] = w;
adj[u].push_back(v);
adj[v].push_back(u); // 反向边
};
addFlowEdge(0, 1, 16);
addFlowEdge(0, 2, 13);
addFlowEdge(1, 2, 10);
addFlowEdge(1, 3, 12);
addFlowEdge(2, 1, 4);
addFlowEdge(2, 4, 14);
addFlowEdge(3, 2, 9);
addFlowEdge(3, 5, 20);
addFlowEdge(4, 3, 7);
addFlowEdge(4, 5, 4);
FordFulkerson ff;
int maxFlow = ff.maxFlow(0, 5, capacity, adj);
cout << "最大流: " << maxFlow << endl;
cout << "\n12. 匈牙利算法测试:" << endl;
vector<vector<int>> bpGraph = {
{1, 1, 0, 0, 0},
{1, 0, 0, 0, 0},
{0, 1, 1, 0, 0},
{0, 0, 1, 1, 0},
{0, 0, 0, 1, 1}
};
Hungarian hungarian;
vector<pair<int, int>> matches = hungarian.maxBipartiteMatching(bpGraph);
cout << "匹配对: ";
for (auto& match : matches) {
cout << "(" << match.first << "," << match.second << ") ";
}
cout << endl;
cout << "\n13. A*算法测试:" << endl;
WeightedGraph astarGraph(5);
vector<pair<int, int>> coordinates = {
{0, 0}, {1, 1}, {2, 2}, {3, 1}, {4, 0}
};
astarGraph.addEdge(0, 1, 1);
astarGraph.addEdge(0, 2, 2);
astarGraph.addEdge(1, 2, 1);
astarGraph.addEdge(1, 3, 1);
astarGraph.addEdge(2, 3, 1);
astarGraph.addEdge(3, 4, 1);
AStar astar;
vector<int> aStarPath = astar.aStar(0, 4, astarGraph, coordinates);
cout << "A*路径: ";
for (int v : aStarPath) cout << v << " ";
cout << endl;
}
int main() {
cout << fixed << setprecision(2);
// 运行所有算法测试
testGraphAlgorithms();
// 额外测试
cout << "\n=== 额外测试: 检测环 ===" << endl;
Graph cyclicGraph(4);
cyclicGraph.addEdge(0, 1);
cyclicGraph.addEdge(1, 2);
cyclicGraph.addEdge(2, 3);
cyclicGraph.addEdge(3, 1); // 创建环
TopologicalSort ts;
vector<int> tsResult = ts.dfsSort(cyclicGraph);
if (tsResult.empty()) {
cout << "图中有环" << endl;
} else {
cout << "拓扑排序: ";
for (int v : tsResult) cout << v << " ";
cout << endl;
}
cout << "\n=== 额外测试: 最短路径 ===" << endl;
WeightedGraph spGraph(6);
spGraph.addEdge(0, 1, 5);
spGraph.addEdge(0, 2, 3);
spGraph.addEdge(1, 3, 6);
spGraph.addEdge(1, 2, 2);
spGraph.addEdge(2, 4, 4);
spGraph.addEdge(2, 5, 2);
spGraph.addEdge(2, 3, 7);
spGraph.addEdge(3, 4, 1);
spGraph.addEdge(4, 5, 3);
vector<int> shortestPath = dijkstra.dijkstra(spGraph, 0);
cout << "从顶点0到各顶点的最短距离:" << endl;
for (int i = 0; i < shortestPath.size(); i++) {
cout << "0 -> " << i << ": ";
if (shortestPath[i] == INF) cout << "INF" << endl;
else cout << shortestPath[i] << endl;
}
return 0;
}
算法性能对比表
| 算法 | 时间复杂度 | 空间复杂度 | 适用图类型 | 应用场景 |
|---|---|---|---|---|
| DFS | O(V+E) | O(V) | 有向/无向 | 拓扑排序、检测环、连通分量 |
| BFS | O(V+E) | O(V) | 有向/无向 | 最短路径(无权)、层级遍历 |
| 拓扑排序 | O(V+E) | O(V) | 有向无环图 | 任务调度、课程安排 |
| Dijkstra | O((V+E)logV) | O(V) | 非负权有向/无向 | 单源最短路径 |
| Bellman-Ford | O(VE) | O(V) | 有向(可负权) | 单源最短路径、检测负环 |
| Floyd-Warshall | O(V³) | O(V²) | 有向/无向 | 所有顶点对最短路径 |
| Prim | O((V+E)logV) | O(V) | 连通无向 | 最小生成树(稠密图) |
| Kruskal | O(ElogE) | O(V) | 连通无向 | 最小生成树(稀疏图) |
| Kosaraju | O(V+E) | O(V) | 有向 | 强连通分量 |
| Tarjan | O(V+E) | O(V) | 有向/无向 | 强连通分量、桥、割点 |
| Ford-Fulkerson | O(E * max_flow) | O(V) | 有向 | 最大流 |
| Edmonds-Karp | O(VE²) | O(V) | 有向 | 最大流(BFS) |
| Dinic | O(V²E) | O(V+E) | 有向 | 最大流(分层图) |
| 匈牙利算法 | O(VE) | O(V) | 二分图 | 最大匹配 |
| A*搜索 | O(b^d) | O(b^d) | 有向/无向 | 启发式路径规划 |
算法选择指南
-
遍历图
- 基本遍历 → DFS/BFS
- 检测环 → DFS/拓扑排序
- 连通分量 → DFS/Kosaraju/Tarjan
-
最短路径
- 无权图 → BFS
- 非负权单源 → Dijkstra
- 可负权单源 → Bellman-Ford
- 所有顶点对 → Floyd-Warshall
- 启发式搜索 → A*
-
最小生成树
- 稠密图 → Prim
- 稀疏图 → Kruskal
-
网络流
- 简单实现 → Ford-Fulkerson
- 一般情况 → Edmonds-Karp
- 高效实现 → Dinic
-
匹配问题
- 二分图匹配 → 匈牙利算法
- 一般图匹配 → 开花算法(未实现)
-
关键组件
- 强连通分量 → Kosaraju/Tarjan
- 桥和割点 → Tarjan
- 拓扑排序 → Kahn/DFS
编译运行
# 编译
g++ -std=c++17 -O2 graph_algorithms.cpp -o graph
# 运行
./graph
输出示例
=== 图算法测试 ===
1. DFS测试:
DFS遍历: 0 1 3 5 2 4
2. BFS测试:
BFS遍历: 0 1 2 3 4 5
3. 拓扑排序测试:
拓扑排序: 0 2 4 1 3 5
4. Dijkstra算法测试:
从0到各顶点的最短距离: 0->0=0 0->1=3 0->2=1 0->3=4 0->4=7
5. Bellman-Ford算法测试:
Bellman-Ford距离: 0 3 1 4 7
6. Floyd-Warshall算法测试:
所有顶点对最短距离:
0 3 1 4 7
INF 0 INF 1 4
INF 2 0 3 6
INF INF INF 0 3
INF INF INF INF 0
7. Prim算法测试:
Prim MST 边:
0 - 1
1 - 2
0 - 3
1 - 4
Prim MST总权重: 16
8. Kruskal算法测试:
Kruskal MST 边:
0 - 1 : 2
1 - 2 : 3
1 - 4 : 5
0 - 3 : 6
Kruskal MST总权重: 16
9. Kosaraju算法测试:
强连通分量:
分量0: 4
分量1: 3
分量2: 0 2 1
10. Tarjan算法测试:
Tarjan算法找到的SCC:
分量0: 4
分量1: 3
分量2: 1 2 0
11. Ford-Fulkerson最大流测试:
最大流: 23
12. 匈牙利算法测试:
最大匹配数: 3
匹配对: (0,0) (2,1) (3,2)
13. A*算法测试:
A*路径: 0 1 3 4
这个实现包含了15种经典图算法的完整C++代码,涵盖遍历、最短路径、最小生成树、网络流、匹配等核心图算法。所有算法都有详细的注释和测试代码,可以直接编译运行。