跳到主要内容
极客日志极客日志
首页博客AI提示词GitHub精选代理工具
搜索
|注册
博客列表
C++算法

数据结构:图论基础

综述由AI生成图论的基础概念,包括顶点、边、有向图与无向图等基本定义。阐述了图的类型(稀疏、稠密、加权等)及表示方法(邻接矩阵、邻接表)。提供了基于 C++ 的邻接矩阵和邻接表实现代码示例,涵盖初始化、添加边等核心接口。最后总结了图论的应用场景及后续学习方向。

RustyLab发布于 2026/3/21更新于 2026/5/215 浏览
数据结构:图论基础

图的概念

图(Graph)是离散数学中的一种重要数据结构,用于描述对象(称为顶点,或节点)之间的关系(称为边)。图可以表示各种事物之间的连接关系,比如网络中的路由器、社交网络中的用户、城市之间的道路等。

图的基本概念

  1. 顶点(Vertex):
    • 图中的基本单位,也称为节点(Node)。一个图通常由若干个顶点组成,用来表示实体或对象。
  2. 边(Edge):
    • 顶点之间的连接关系称为边。边可以是有方向的(有向图)或无方向的(无向图)。在无向图中,边表示双向关系;在有向图中,边表示单向关系。
  3. 有向图(Directed Graph, Digraph):
    • 边具有方向性,表示从一个顶点指向另一个顶点的单向连接。
  4. 无向图(Undirected Graph):
    • 边没有方向,表示两个顶点之间的对称关系,如'相邻'或'连接'。
  5. 邻接(Adjacency):
    • 如果两个顶点之间有一条边连接,这两个顶点称为邻接的。
  6. 度(Degree):
    • 一个顶点的度是连接到该顶点的边的数目。在有向图中,区分入度(In-degree)和出度(Out-degree),分别表示进入该顶点和从该顶点发出的边的数量。

图的类型

  1. 稀疏图(Sparse Graph):
    • 边的数量远远少于顶点数量的平方(E << V²),大部分顶点之间没有边连接。
  2. 稠密图(Dense Graph):
    • 边的数量接近顶点数量的平方(E ≈ V²),大多数顶点之间有边连接。
  3. 加权图(Weighted Graph):
    • 图中的每条边带有一个权重,用来表示顶点之间的某种度量,如距离、成本或容量。
  4. 连通图(Connected Graph):
    • 对于无向图,若任意两个顶点之间都有路径相连,则称为连通图。有向图中则需区分强连通和弱连通。
  5. 简单图(Simple Graph):
    • 不包含自环和多重边的图。自环是指从顶点到自身的边,多重边是指两个顶点之间存在多条边。
  6. 完全图(Complete Graph):
    • 图中任意两个顶点之间都有边相连,通常表示为 Kn,其中 n 是顶点数。

图的表示方法

  1. 邻接矩阵(Adjacency Matrix):
    • 使用一个二维矩阵来表示顶点之间的连接关系,矩阵中的元素表示边的存在性或权重。
  2. 邻接表(Adjacency List):
    • 对每个顶点,维护一个链表(或数组)来存储与之相邻的顶点列表,适合稀疏图。
  3. 边集列表(Edge List):
    • 直接列出图中所有的边,用边的起点和终点来描述,适合图的遍历或算法中的具体操作。

图的相关基本概念

在理解图的结构和操作时,有一些与图密切相关的概念有助于更好地分析和处理图的算法和应用。

1. 路径(Path)

路径是在图中从一个顶点到另一个顶点的行进序列,它由一系列边和顶点组成。根据图的类型和要求,路径可以分为几类:

  • 简单路径(Simple Path):路径中的顶点不重复出现。
  • 回路(Cycle):路径的起点和终点是同一个顶点,且路径中的其他顶点不重复。

2. 连通性(Connectivity)

图的连通性描述了图中顶点与顶点之间的可达性。

  • 连通图(Connected Graph):对于无向图,如果从任意一个顶点能到达其他所有顶点,则图是连通的。
  • 强连通图(Strongly Connected Graph):对于有向图,如果任意两个顶点之间都有路径相通,则图是强连通的。
  • 弱连通图(Weakly Connected Graph):对于有向图,如果将其所有边看作无向边,能够使整个图连通,则图是弱连通的。

3. 图的度(Degree)

图中一个顶点的度表示与该顶点连接的边的数量。

  • 入度(In-degree):有向图中指向该顶点的边的数量。
  • 出度(Out-degree):有向图中从该顶点发出的边的数量。

4. 子图(Subgraph)

子图是从原始图中选取部分顶点和边构成的图。常见的子图类型包括:

  • 生成子图(Spanning Subgraph):包含图的所有顶点,但边可能有所删减。
  • 诱导子图(Induced Subgraph):包含图中某些顶点及这些顶点之间的所有边。

5. 生成树(Spanning Tree)

生成树是无向图的一个子图,包含图中的所有顶点,且是一个树形结构(无环、连通)。

  • 最小生成树(Minimum Spanning Tree, MST):在加权图中,生成树的边权和最小的生成树。

6. 二分图(Bipartite Graph)

二分图是一种特殊的图,可以将顶点集合分为两个不相交的子集,且所有边都连接两个不同子集中的顶点,而子集中没有内部连接。

7. 拓扑排序(Topological Sort)

对于有向无环图(DAG),拓扑排序是一种将顶点按依赖关系线性排序的算法,通常用于解决调度、依赖管理等问题。

8. 欧拉图和哈密顿图

  • 欧拉路径(Eulerian Path):经过图中每条边一次且仅一次的路径。如果这样的路径是闭合的,即路径的起点和终点相同,则称为欧拉回路(Eulerian Circuit)。
  • 哈密顿路径(Hamiltonian Path):经过图中每个顶点一次且仅一次的路径。如果这样的路径是闭合的,则称为哈密顿回路(Hamiltonian Circuit)。

实现图

邻接矩阵实现图

如果是无向图的话,那么邻接矩阵就是沿着对角线对称的。

如果是无向图的话,那么就可以通过压缩只存储一半。 除了需要一个存储权值的邻接矩阵我们还需要一个 vector 来存储顶点,如果涉及到邻接矩阵,那么就会涉及到下标,所以我们应该还需要一个顶点映射下标的 map。

邻接矩阵的基本框架
// 顶点 weight 有向图/无向图
template<class V, class W, W MAX_W = INT_MAX, bool Direction = false>
class Graph {
public:
private:
    vector<V> _vertexs;      // 顶点集合
    map<V, int> _indexMap;   // 顶点对应下标的关系(顶点---->下标)
    vector<vector<W>> _matrix; // 邻接矩阵
};
核心接口

初始化

Graph(const V* a, size_t n) {
    // 开辟 n 个空间
    _vertexs.resize(n);
    for (size_t i = 0; i < n; i++) {
        _vertexs[i] = a[i];
        // 顶点----->下标
        _indexMap[a[i]] = i;
    }
    _matrix.resize(n);
    // 权值用 MAX_W 初始化
    for (size_t i = 0; i < n; i++)
        _matrix[i].resize(n, MAX_W);
}

获取顶点的下标

size_t GetVertexIndex(const V& v) {
    auto it = _indexMap.find(v);
    if (it != _indexMap.end())
        return it->second;
    else {
        // 抛异常出去
        throw invalid_argument("顶点不存在");
        // 过编译器逻辑
        return -1;
    }
}

通过 map 的接口 find 找到 v 对应的下标,如果 it 为 end(),说明没有这个顶点,直接抛异常,如果存在,则返回对应的下标。

添加边

// 原点 目标点 权值
void AddEdge(const V& src, const V& dst, const W& w) {
    // 获取原点下标
    size_t srci = GetVertexIndex(src);
    size_t dsti = GetVertexIndex(dst);
    _matrix[srci][dsti] = w;
    // 无向图添加两个权值
    if (Direction == false)
        _matrix[dsti][srci] = w;
}

找到原点和目标点对应的下标,如果是有向图的话,只需要添加原点到目标点的边,如果是无向图的话,就需要反向添加一下目标点到原点的边了。

邻接表实现图

邻接表是图的一种存储表示方法,常用于存储稀疏图(即边数相对较少的图)。它通过每个顶点对应一个链表或数组来记录与其相邻的顶点。邻接表的空间复杂度相对较小,适合存储大规模图。

对于无向图来说不存在入度和出度的概念,所以只需要一个邻接表表示,下标的邻接表就是用来表示边的集合。

拿第一个点为例,这里使用一个结构体来维护的,这个结构体中有指向一下下一个边集的指针,还有一个权值和目标点的下标。 类似于哈希桶的那个做法。

邻接表实现图的基本框架
template<class W>
struct Edge {
    int _dsti;     // 目标点的下标
    W _w;          // 权值
    Edge<W>* _next;
    Edge(int dsti, const W& w) : _dsti(dsti), _w(w), _next(nullptr) {}
};

// 顶点 weight 有向图/无向图
template<class V, class W, bool Direction = false>
class Graph {
    typedef Edge<W> Edge;
public:
private:
    vector<V> _vertexs;      // 顶点集合
    map<V, int> _indexMap;   // 顶点对应下标的关系(顶点---->下标)
    vector<Edge*> _tables;   // 邻接表
};
核心接口

初始化

Graph(const V* a, size_t n) {
    // 开辟 n 个空间
    _vertexs.resize(n);
    for (size_t i = 0; i < n; i++) {
        _vertexs[i] = a[i];
        // 顶点----->下标
        _indexMap[a[i]] = i;
    }
    _tables.resize(n, nullptr); // 给 tables 开辟 n 个空间(n 个顶点)
}

获取顶点对应下标

size_t GetVertexIndex(const V& v) {
    auto it = _indexMap.find(v);
    if (it != _indexMap.end())
        return it->second;
    else {
        // 抛异常出去
        throw invalid_argument("顶点不存在");
        // 过编译器逻辑
        return -1;
    }
}

添加边

void AddEdge(const V& src, const V& dst, const W& w) {
    // 获取原点下标
    size_t srci = GetVertexIndex(src);
    size_t dsti = GetVertexIndex(dst);
    // 1 --> 2
    // 原点到目标的权值
    Edge* eg = new Edge(dsti, w);
    // 这里进行头插
    eg->_next = _tables[srci];
    _tables[srci] = eg;
    // 无向图进行特殊处理
    // 2 --> 1
    if (Direction == false) {
        // 反向指向
        Edge* eg = new Edge(srci, w);
        eg->_next = _tables[dsti];
        _tables[dsti] = eg;
    }
}

这个和邻接矩阵就不一样,邻接表中添加边应该先创建一个对应边的结构体,然后将这个结构体头插到原点对应下标的边集的位置上,如果是无向图的话,需要反向添加一条目标点到原点的边。注意:头插之后需要改变头的位置。

总结

通过本篇文章的介绍,我们初步了解了图的基本概念、图的表示方法(如邻接矩阵和邻接表)、以及图中的各种基本性质。图论作为计算机科学和数学中的一个重要分支,其应用范围广泛,从网络设计到路径规划,都有着广泛的应用场景。

在接下来的学习中,我们可以进一步探讨更高级的图算法,如最短路径算法(Dijkstra、Bellman-Ford 等)、图的遍历算法(深度优先搜索、广度优先搜索)、以及图的连通性和最小生成树等高级主题。这些内容将为解决更复杂的实际问题提供坚实的理论基础。

目录

  1. 图的概念
  2. 图的基本概念
  3. 图的类型
  4. 图的表示方法
  5. 图的相关基本概念
  6. 1. 路径(Path)
  7. 2. 连通性(Connectivity)
  8. 3. 图的度(Degree)
  9. 4. 子图(Subgraph)
  10. 5. 生成树(Spanning Tree)
  11. 6. 二分图(Bipartite Graph)
  12. 7. 拓扑排序(Topological Sort)
  13. 8. 欧拉图和哈密顿图
  14. 实现图
  15. 邻接矩阵实现图
  16. 邻接矩阵的基本框架
  17. 核心接口
  18. 邻接表实现图
  19. 邻接表实现图的基本框架
  20. 核心接口
  21. 总结
  • 💰 8折买阿里云服务器限时8折了解详情
  • GPT-5.5 超高智商模型1元抵1刀ChatGPT中转购买
  • 代充Chatgpt Plus/pro 帐号了解详情
  • 🤖 一键搭建Deepseek满血版了解详情
  • 一键打造专属AI 智能体了解详情
极客日志微信公众号二维码

微信扫一扫,关注极客日志

微信公众号「极客日志V2」,在微信中扫描左侧二维码关注。展示文案:极客日志V2 zeeklog

更多推荐文章

查看全部
  • MySQL 备份与恢复实战:XtraBackup 和 mysqldump 详解
  • 字节开源 Hyper-SD:Stable Diffusion 1 步高清加速模型详解
  • FVTracker 基于 Python 的基金估值跟踪工具
  • GHCR.io 入门指南:GitHub 容器仓库使用教程
  • Cursor AI 辅助 Java 项目开发与配置指南
  • 配电房智能辅助监控系统及站端监控设备详解
  • 编写 ChatGPT 指令(Prompt)的万能模板及实用示例
  • Git 安装配置与基础工作流实战指南
  • YOLOv8 旋转框角度回归优化:CSL 与 DCL 编码实战
  • 字节跳动前端开发面试核心考点与实战指南
  • SCADA Engine:基于 Vue3 的开源工业级组态引擎
  • 近五年体内微/纳米机器人在肿瘤精准治疗中的应用:聚焦胶质母细胞瘤
  • Open-WebUI 管理员面板深度拆解与配置指南
  • MCP Gateway:零侵入式 API 至 MCP 协议转换网关
  • KrLongAI 旗博士:本地部署 AI 数字人口播视频自动化工程
  • Linux 安装 Claude Code 及 VS Code SSH 远程集成配置
  • Flutter 三方库 flutter_cors 应对鸿蒙 Web 与混合开发中的跨域挑战
  • ARIS 开源:基于 Claude Code 的自动化科研与论文工作流
  • Coze 打造专属 AI 应用:从智能体开发到 Web 部署
  • 用 Claude Code 构建 AI 内容创作工作流:从灵感到发布自动化

相关免费在线工具

  • 加密/解密文本

    使用加密算法(如AES、TripleDES、Rabbit或RC4)加密和解密文本明文。 在线工具,加密/解密文本在线工具,online

  • Gemini 图片去水印

    基于开源反向 Alpha 混合算法去除 Gemini/Nano Banana 图片水印,支持批量处理与下载。 在线工具,Gemini 图片去水印在线工具,online

  • Base64 字符串编码/解码

    将字符串编码和解码为其 Base64 格式表示形式即可。 在线工具,Base64 字符串编码/解码在线工具,online

  • Base64 文件转换器

    将字符串、文件或图像转换为其 Base64 表示形式。 在线工具,Base64 文件转换器在线工具,online

  • Markdown转HTML

    将 Markdown(GFM)转为 HTML 片段,浏览器内 marked 解析;与 HTML转Markdown 互为补充。 在线工具,Markdown转HTML在线工具,online

  • HTML转Markdown

    将 HTML 片段转为 GitHub Flavored Markdown,支持标题、列表、链接、代码块与表格等;浏览器内处理,可链接预填。 在线工具,HTML转Markdown在线工具,online