跳转至

📖 章节简介

本章将介绍C++的图数据结构,包括图的表示、DFS、BFS和最短路径算法。

图表示对比示意

上图对比了邻接矩阵与邻接表两种表示方式,帮助理解它们在空间复杂度与适用场景上的差别。

📊 图的表示

1. 邻接矩阵

C++
// 邻接矩阵
#include <iostream>
#include <vector>
using namespace std;

class Graph {
private:
    int numVertices;
    vector<vector<int>> adjMatrix;

public:
    Graph(int vertices) {
        numVertices = vertices;
        adjMatrix.resize(vertices, vector<int>(vertices, 0));
    }

    void addEdge(int u, int v) {
        adjMatrix[u][v] = 1;
        adjMatrix[v][u] = 1;  // 无向图
    }

    void display() {
        for (int i = 0; i < numVertices; i++) {
            for (int j = 0; j < numVertices; j++) {
                cout << adjMatrix[i][j] << " ";
            }
            cout << endl;
        }
    }

    bool hasEdge(int u, int v) {
        return adjMatrix[u][v] == 1;
    }
};

int main() {
    Graph graph(4);

    // 添加边
    graph.addEdge(0, 1);
    graph.addEdge(0, 2);
    graph.addEdge(1, 2);
    graph.addEdge(2, 3);

    // 显示邻接矩阵
    cout << "邻接矩阵:" << endl;
    graph.display();

    // 检查边
    cout << "\n(0,1)有边: " << (graph.hasEdge(0, 1) ? "是" : "否") << endl;
    cout << "(1,3)有边: " << (graph.hasEdge(1, 3) ? "是" : "否") << endl;

    return 0;
}

2. 邻接表

C++
// 邻接表
#include <iostream>
#include <vector>
#include <list>
using namespace std;

class Graph {
private:
    int numVertices;
    vector<list<int>> adjList;

public:
    Graph(int vertices) {
        numVertices = vertices;
        adjList.resize(vertices);
    }

    void addEdge(int u, int v) {
        adjList[u].push_back(v);
        adjList[v].push_back(u);  // 无向图
    }

    void display() {
        for (int i = 0; i < numVertices; i++) {
            cout << "顶点" << i << ": ";
            for (int neighbor : adjList[i]) {
                cout << neighbor << " -> ";
            }
            cout << "NULL" << endl;
        }
    }

    vector<int> getNeighbors(int vertex) {
        return vector<int>(adjList[vertex].begin(), adjList[vertex].end());
    }
};

int main() {
    Graph graph(4);

    // 添加边
    graph.addEdge(0, 1);
    graph.addEdge(0, 2);
    graph.addEdge(1, 2);
    graph.addEdge(2, 3);

    // 显示邻接表
    cout << "邻接表:" << endl;
    graph.display();

    // 获取邻居
    cout << "\n顶点2的邻居:";
    vector<int> neighbors = graph.getNeighbors(2);
    for (int neighbor : neighbors) {
        cout << neighbor << " ";
    }

    return 0;
}

🔍 深度优先搜索(DFS)

1. DFS实现

C++
// DFS实现
#include <iostream>
#include <vector>
#include <stack>
using namespace std;

// ⚠️ 线程安全/可重入性提示:visited 作为成员变量存储遍历状态,
// 使得 DFS/BFS 方法不可重入——不能在多线程中并发调用,也不能在遍历回调中再次调用。
// 更好的做法是将 visited 作为局部变量传入遍历方法。
class Graph {
private:
    int numVertices;
    vector<vector<int>> adjList;
    vector<bool> visited;

public:
    Graph(int vertices) {
        numVertices = vertices;
        adjList.resize(vertices);
        visited.resize(vertices, false);
    }

    void addEdge(int u, int v) {
        adjList[u].push_back(v);
        adjList[v].push_back(u);
    }

    void DFS(int start) {
        fill(visited.begin(), visited.end(), false);

        cout << "DFS遍历结果:";
        DFSUtil(start);
        cout << endl;
    }

    void DFSUtil(int vertex) {
        visited[vertex] = true;
        cout << vertex << " ";

        for (int neighbor : adjList[vertex]) {
            if (!visited[neighbor]) {
                DFSUtil(neighbor);
            }
        }
    }

    void DFSIterative(int start) {
        fill(visited.begin(), visited.end(), false);

        stack<int> s;
        s.push(start);
        visited[start] = true;

        cout << "DFS迭代遍历结果:";
        while (!s.empty()) {
            int vertex = s.top();
            s.pop();
            cout << vertex << " ";

            for (int neighbor : adjList[vertex]) {
                if (!visited[neighbor]) {
                    s.push(neighbor);
                    visited[neighbor] = true;
                }
            }
        }
        cout << endl;
    }
};

int main() {
    Graph graph(6);

    // 添加边
    graph.addEdge(0, 1);
    graph.addEdge(0, 2);
    graph.addEdge(1, 3);
    graph.addEdge(2, 4);
    graph.addEdge(3, 5);
    graph.addEdge(4, 5);

    // DFS遍历
    graph.DFS(0);

    // DFS迭代遍历
    graph.DFSIterative(0);

    return 0;
}

🌊 广度优先搜索(BFS)

1. BFS实现

C++
// BFS实现
#include <iostream>
#include <vector>
#include <queue>
using namespace std;

class Graph {
private:
    int numVertices;
    vector<vector<int>> adjList;
    vector<bool> visited;

public:
    Graph(int vertices) {
        numVertices = vertices;
        adjList.resize(vertices);
        visited.resize(vertices, false);
    }

    void addEdge(int u, int v) {
        adjList[u].push_back(v);
        adjList[v].push_back(u);
    }

    void BFS(int start) {
        fill(visited.begin(), visited.end(), false);

        queue<int> q;
        q.push(start);
        visited[start] = true;

        cout << "BFS遍历结果:";
        while (!q.empty()) {
            int vertex = q.front();
            q.pop();
            cout << vertex << " ";

            for (int neighbor : adjList[vertex]) {
                if (!visited[neighbor]) {
                    q.push(neighbor);
                    visited[neighbor] = true;
                }
            }
        }
        cout << endl;
    }

    void BFSLevelOrder(int start) {
        fill(visited.begin(), visited.end(), false);

        queue<int> q;
        q.push(start);
        visited[start] = true;

        cout << "BFS层序遍历结果:" << endl;
        int level = 0;

        while (!q.empty()) {
            int levelSize = q.size();

            cout << "第" << level << "层: ";
            for (int i = 0; i < levelSize; i++) {
                int vertex = q.front();
                q.pop();
                cout << vertex << " ";

                for (int neighbor : adjList[vertex]) {
                    if (!visited[neighbor]) {
                        q.push(neighbor);
                        visited[neighbor] = true;
                    }
                }
            }
            cout << endl;
            level++;
        }
    }
};

int main() {
    Graph graph(6);

    // 添加边
    graph.addEdge(0, 1);
    graph.addEdge(0, 2);
    graph.addEdge(1, 3);
    graph.addEdge(2, 4);
    graph.addEdge(3, 5);
    graph.addEdge(4, 5);

    // BFS遍历
    graph.BFS(0);

    // BFS层序遍历
    graph.BFSLevelOrder(0);

    return 0;
}

🛣️ 最短路径

1. Dijkstra算法

C++
// Dijkstra算法
#include <iostream>
#include <vector>
#include <queue>
#include <climits>
using namespace std;

class Graph {
private:
    int numVertices;
    vector<vector<pair<int, int>>> adjList;  // (neighbor, weight)

public:
    Graph(int vertices) {
        numVertices = vertices;
        adjList.resize(vertices);
    }

    void addEdge(int u, int v, int weight) {
        adjList[u].push_back({v, weight});
        adjList[v].push_back({u, weight});  // 无向图
    }

    void dijkstra(int start) {
        vector<int> distance(numVertices, INT_MAX);
        vector<bool> visited(numVertices, false);
        vector<int> parent(numVertices, -1);

        distance[start] = 0;

        priority_queue<pair<int, int>,
                       vector<pair<int, int>>,
                       greater<pair<int, int>>> pq;
        pq.push({0, start});

        while (!pq.empty()) {
            int u = pq.top().second;
            pq.pop();

            if (visited[u]) {
                continue;
            }

            visited[u] = true;

            for (auto& edge : adjList[u]) {
                int v = edge.first;
                int weight = edge.second;

                if (!visited[v] && distance[u] != INT_MAX &&
                    distance[u] + weight < distance[v]) {
                    distance[v] = distance[u] + weight;
                    parent[v] = u;
                    pq.push({distance[v], v});
                }
            }
        }

        // 输出最短路径
        cout << "顶点\t距离\t路径" << endl;
        for (int i = 0; i < numVertices; i++) {
            cout << i << "\t" << distance[i] << "\t";

            if (distance[i] != INT_MAX) {
                // 回溯路径
                vector<int> path;
                int current = i;
                while (current != -1) {
                    path.push_back(current);
                    current = parent[current];
                }

                for (int j = path.size() - 1; j >= 0; j--) {
                    cout << path[j];
                    if (j > 0) {
                        cout << " -> ";
                    }
                }
            }
            cout << endl;
        }
    }
};

int main() {
    Graph graph(5);

    // 添加边
    graph.addEdge(0, 1, 4);
    graph.addEdge(0, 2, 2);
    graph.addEdge(1, 2, 1);
    graph.addEdge(1, 3, 7);
    graph.addEdge(2, 3, 3);
    graph.addEdge(3, 4, 1);
    graph.addEdge(4, 3, 2);

    // Dijkstra算法
    graph.dijkstra(0);

    return 0;
}

💡 最佳实践

1. 图表示选择

C++
// 图表示选择
int main() {
    // ✅ 好的做法:根据图的特点选择表示方式
    // 稀疏图:使用邻接表
    // 稠密图:使用邻接矩阵

    // ❌ 不好的做法:不根据情况选择
    // 总是使用邻接矩阵(稀疏图浪费空间)
    // 总是使用邻接表(稠密图查询慢)

    return 0;
}

2. 算法选择

C++
// 算法选择
int main() {
    // ✅ 好的做法:根据需求选择算法
    // 最短路径:Dijkstra算法
    // 最小生成树:Prim或Kruskal算法
    // 拓扑排序:Kahn算法

    // ❌ 不好的做法:不根据需求选择
    // 总是使用DFS(BFS可能更合适)
    // 总是使用Dijkstra(图无权重时BFS更合适)

    return 0;
}

📝 练习题

基础题

  1. 什么是图?
  2. 邻接矩阵和邻接表有什么区别?
  3. DFS和BFS有什么区别?

进阶题

  1. 实现图的DFS和BFS。
  2. 实现Dijkstra算法。
  3. 实现拓扑排序。

实践题

  1. 创建一个简单的社交网络。
  2. 实现一个路由算法。
  3. 构建一个迷宫求解器。

📚 推荐阅读

🔗 下一章

排序算法 - 学习C++的排序算法。