当然可以,以下是《数据结构与算法之美:图》专题笔记,涵盖图的定义、分类、常用存储结构、经典算法及其工程实现,适合用于刷题、备考、系统掌握图的相关知识。


📘 数据结构与算法之美:图


🧠 一、图的基本概念

图(Graph)是一种非线性数据结构,由**顶点(Vertex)边(Edge)**组成,用于描述实体之间的各种关系。

1.1 图的术语

  • 顶点(Vertex):图中的一个节点
  • 边(Edge):连接两个顶点的线
  • 度(Degree):顶点关联的边的数量
    • 入度:指向该顶点的边数
    • 出度:从该顶点出发的边数
  • 路径(Path):从一个顶点到另一个顶点的边序列
  • 回路(Cycle):起点和终点相同的路径

🔍 二、图的分类

类型描述
无向图边没有方向(A-B 等于 B-A)
有向图(有向边)边具有方向(A→B ≠ B→A)
带权图边有权重(如距离、费用)
稀疏图边远少于顶点的平方
稠密图边接近顶点的平方
连通图任意两个顶点可达
强连通图(有向图)任意两个顶点相互可达

🗃️ 三、图的存储结构

3.1 邻接矩阵(Adjacency Matrix)

  • 使用二维数组 g[i][j] 表示从顶点 i 到 j 是否有边(或权重)
  • 空间复杂度:O(n²)
int graph[N][N]; // N 为顶点数

3.2 邻接表(Adjacency List)

  • 用链表或数组数组存储每个顶点的邻接顶点
  • 空间复杂度:O(n + e),适用于稀疏图
vector<int> adj[N]; // 邻接表存储无权图
vector<pair<int, int>> adj[N]; // 邻接表存储带权图

📐 四、图的遍历算法

4.1 深度优先搜索(DFS)

  • 类似树的先序遍历,回溯实现
void dfs(int u) {
    visited[u] = true;
    for (int v : adj[u]) {
        if (!visited[v]) dfs(v);
    }
}

4.2 广度优先搜索(BFS)

  • 类似树的层序遍历,使用队列实现
void bfs(int start) {
    queue<int> q;
    visited[start] = true;
    q.push(start);
    while (!q.empty()) {
        int u = q.front(); q.pop();
        for (int v : adj[u]) {
            if (!visited[v]) {
                visited[v] = true;
                q.push(v);
            }
        }
    }
}

🚦 五、最短路径算法

5.1 Dijkstra 算法(正权图)

vector<int> dijkstra(int start) {
    vector<int> dist(n, INF);
    priority_queue<pair<int, int>, vector<pair<int,int>>, greater<>> pq;
    dist[start] = 0;
    pq.push({0, start});
    
    while (!pq.empty()) {
        auto [d, u] = pq.top(); pq.pop();
        if (d > dist[u]) continue;
        for (auto [v, w] : adj[u]) {
            if (dist[v] > d + w) {
                dist[v] = d + w;
                pq.push({dist[v], v});
            }
        }
    }
    return dist;
}

5.2 Bellman-Ford 算法(可处理负权)

  • 时间复杂度 O(VE),可检测负环

5.3 Floyd-Warshall 算法(多源最短路径)

  • 三重循环,适用于图顶点不多的场景
  • 时间复杂度 O(n³)

🕸️ 六、图的其他经典算法

6.1 拓扑排序(有向无环图 DAG)

// Kahn 算法,使用入度统计实现
queue<int> q;
vector<int> topo;
for (int i = 0; i < n; i++)
    if (indegree[i] == 0) q.push(i);

while (!q.empty()) {
    int u = q.front(); q.pop();
    topo.push_back(u);
    for (int v : adj[u]) {
        if (--indegree[v] == 0)
            q.push(v);
    }
}

6.2 最小生成树(MST)

  • Kruskal 算法:贪心 + 并查集
  • Prim 算法:优先队列维护最小边

6.3 强连通分量(SCC)

  • Tarjan 算法(DFS 时间戳 + 栈)
  • Kosaraju 算法(两次 DFS)

🧩 七、常见图论题型与技巧

题型分类解法提示
连通块个数DFS/BFS 求连通块
最短路径Dijkstra / SPFA / Floyd
拓扑排序DAG + 入度法 or DFS
判断环DFS 回溯检测 or 并查集
图着色问题贪心 / DFS
二分图染色DFS 两色染色法
社交网络分析连通子图统计、图中心查找
欧拉路径 / 回路度数统计 + DFS

🧠 八、图相关 LeetCode 题目推荐

题号题目类型
200岛屿数量DFS/BFS
207课程表(拓扑排序)拓扑排序
210课程表 II(输出拓扑序列)拓扑排序
743网络延迟时间(单源最短路径)Dijkstra
133克隆图(图的遍历)DFS
785判断二分图染色法
547省份数量(连通分量)并查集
261判断图是否为树并查集
886可能的二分法染色法

📦 九、图在工程中的应用

  • 地图导航:路径规划(高德/百度地图)
  • 社交网络分析:朋友推荐、社群识别(朋友圈图)
  • 推荐系统:图神经网络(GNN)建模用户-物品关系
  • 流程依赖管理:编译器依赖解析(拓扑排序)
  • 网络通信:路由协议(BGP、OSPF 使用图结构)
  • 知识图谱:概念实体的连接图谱结构

📚 十、参考资料与推荐阅读