图论(1)图数据结构

目录

一、图的定义

1.1 图的基本概念

1.2 图的分类

(1)按边的方向:

(2)按边的权值:

(3)按边的数量和类型:

(4)按连通性:

1.3 图的基本术语

二、图的表示方法

2.1 邻接矩阵(Adjacency Matrix)

2.2 邻接表(Adjacency List)

2.3 边列表(Edge List)

2.4 关联矩阵(Incidence Matrix)

三、图的关系建模

3.1 建模基本思路

3.2 表结构设计

(1)顶点表设计

(2)边表设计(适用于有向图)

(3)标签元数据表设计

(4)无向图处理方法

1 图的基本结构相关概念

2图的类型和性质

3 图的遍历与算法相关概念

4 图的矩阵表示与运算

5 高阶图模型与扩展概念


图论(Graph Theory)是计算机科学中研究“对象之间关系”的重要分支,它以“图”作为基本结构来建模现实世界中各种“点”和“连接”。本篇文章将系统介绍图的基础概念——图的定义图的表示方法,帮助读者构建图论学习的核心框架。

一、图的定义

1.1 图的基本概念

一个图(Graph)是由一组顶点(Vertices)和一组边(Edges)构成的集合结构。用数学符号表示为:

G = (V, E)

其中:

  • V 是图中顶点的集合,通常记作 V = \{v_1, v_2, \ldots, v_n\};

  • E 是图中边的集合,每条边连接两个顶点。

边可以是无向的,也可以是有向的。

1.2 图的分类

图根据其结构特性可以细分为多种类型:

(1)按边的方向:
  • 无向图(Undirected Graph):边不具备方向性,(u, v) 与 (v, u) 等价;

  • 有向图(Directed Graph):边具有方向性,(u, v) 表示从 u 指向 v。

(2)按边的权值:
  • 非加权图(Unweighted Graph):所有边等价;

  • 加权图(Weighted Graph):每条边都有一个权值(如距离、代价等),表示边的“强度”或“成本”。

(3)按边的数量和类型:
  • 简单图(Simple Graph):不含重边和自环;

  • 多重图(Multigraph):允许两个顶点之间存在多条边(重边);

  • 伪图(Pseudograph):允许自环和重边。

(4)按连通性:
  • 连通图(Connected Graph):无向图中任意两个顶点之间至少存在一条路径;

  • 强连通图(Strongly Connected Graph):有向图中任意两点之间都可互达;

  • 弱连通图(Weakly Connected Graph):有向图在忽略方向后是连通的。

1.3 图的基本术语

  • 顶点(Vertex):图中的基本单位;

  • 边(Edge):连接两个顶点的线段,表示关系;

  • 度(Degree)

    • 无向图中:一个顶点的度是连接该顶点的边数;

    • 有向图中:包括入度(In-degree)*和*出度(Out-degree)

  • 路径(Path):由一系列连续边组成的顶点序列;

  • 简单路径(Simple Path):路径中顶点不重复;

  • 环(Cycle):起点和终点相同的简单路径;

  • 自环(Loop):起点和终点为同一顶点的边;

  • 重边(Multiple Edge):连接同一对顶点的多条边;

  • 稀疏图与稠密图

    • 稀疏图:边的数量远小于顶点对的数量;

    • 稠密图:边的数量接近最大边数(例如 n(n-1)/2)。

二、图的表示方法

在计算机中使用图时,必须选择合适的数据结构对其进行编码。常见的图表示方式包括:

2.1 邻接矩阵(Adjacency Matrix)

邻接矩阵是一种二维数组 A[n][n],用来表示顶点之间是否相连。

  • 若 i 与 j 之间有边,则 A[i][j] = 1(或等于权重);

  • 否则 A[i][j] = 0;

  • 无向图的邻接矩阵是对称的;

  • 有向图不一定对称。

示例:

设图 G 有 3 个顶点,边为 (0, 1), (1, 2),邻接矩阵为:

  0 1 2
0 0 1 0
1 1 0 1
2 0 1 0

优点:

  • 查询任意两点是否连接只需 O(1) 时间;

  • 结构简单,适用于稠密图。

缺点:

  • 空间复杂度为 O(n^2),不适用于稀疏图。

2.2 邻接表(Adjacency List)

邻接表用一个数组加多个链表来表示图:

  • 每个顶点拥有一个链表,链表中记录与该顶点相邻的顶点;

  • 对于无向图,每条边需在两个顶点的链表中都出现;

  • 对于有向图,只记录出边。

示例:

0: 1  
1: 0 → 2  
2: 1

优点:

  • 空间复杂度为 O(n + m),适合稀疏图;

  • 遍历某一顶点的邻接点效率高。

缺点:

  • 查询两点之间是否有边需遍历链表,最坏为 O(n)。


2.3 边列表(Edge List)

边列表使用一个列表来记录图中所有边:

  • 每条边表示为一个二元组 (u, v) 或三元组 (u, v, w)(带权);

  • 通常用于图算法中。

示例:

[(0, 1), (1, 2)]

优点:

  • 简单直接,节省空间;

  • 适合用于边排序、生成树等算法。

缺点:

  • 查询效率低,不利于邻接点快速访问。


2.4 关联矩阵(Incidence Matrix)

关联矩阵是一个 n \times m 的矩阵(n 是顶点数,m 是边数):

  • 无向图中,若边 e_j 与顶点 v_i 相连,则 M[i][j] = 1;

  • 有向图中,若 v_i 是起点则 M[i][j] = 1,若 v_i 是终点则 M[i][j] = -1。

优点:

  • 可用于代数图理论分析;

  • 明确表示顶点与边的连接关系。

缺点:

  • 在实际编程中使用不多,空间浪费较大。

表示方法对比:

表示方法空间复杂度查询边是否存在枚举邻接点适用场景
邻接矩阵O(n^2)O(1)O(n)稠密图
邻接表O(n + m)最坏 O(n)O(k)稀疏图
边列表O(m)O(m)不适用构建/排序
关联矩阵O(n \times m)O(m)不适用理论研究

2.5 四种方案的Java定义

package graph;

import java.util.*;
import java.util.concurrent.atomic.AtomicInteger;

/**
 * 顶点定义
 */
class Node {
    private static AtomicInteger nodeIdCounter = new AtomicInteger(0);
    public int id = 0;
    public String name;          // 顶点名称
    public List<Edge> adj;       // 邻接顶点列表
    boolean processed;           // 是否已处理
    public int dist;             // 初始距离设为无穷大
    public Node preNode;         // 最短路径上的前驱顶点

    public Node(String name) {
        this.id = genId();
        this.name = name;
        this.adj = new ArrayList<>();
        this.processed = false;
        this.dist = Integer.MAX_VALUE;
        this.preNode = null;
    }

    synchronized private int genId() {
        return nodeIdCounter.getAndIncrement();
    }

    // 添加邻接边
    public void addEdge(Node to, int weight) {
        this.adj.add(new Edge(this, to, weight));
    }
}


/**
 * 边类定义
 */
class Edge {
    private static AtomicInteger nodeIdCounter = new AtomicInteger(0);

    int id;
    Node from;
    Node to;   // 目标节点
    int weight;    // 边权重

    public Edge(Node from, Node to, int weight) {
        this.id = genId();
        this.from = from;
        this.to = to;
        this.weight = weight;
    }

    synchronized private int genId() {
        return nodeIdCounter.getAndIncrement();
    }
}


/**
 * 图数据结构
 */
public class GraphDS {

    /**
     * 一 邻接矩阵定义
     */
    public static class AdjacencyMatrix {
        private Edge[][] matrix;
        private int vertexCount;

        // 通过强制类型转换创建泛型数组
        public AdjacencyMatrix(int vertexCount) {
            this.vertexCount = vertexCount;
            this.matrix = new Edge[vertexCount][vertexCount];
        }

        public void addEdge(Node from, Node to) {
            // 无向图需要对称设置
            matrix[from.id][to.id] = new Edge(from, to, 1);
        }
    }

    /**
     * 二 邻接表定义
     */
    public static class AdjacencyListByVertex1 {
        static class Vertex {
            public int id;
            public String name;          // 顶点名称
            public List<Vertex> adj;       // 邻接顶点列表
        }
    }

    public static class AdjacencyListByVertex2 {
        static class Vertex {
            public int id;
            public String name;          // 顶点名称
            public List<Edge> adj;       // 邻接顶点边列表
        }

        static class Edge {
            int id;
            Node from;
            Node to;   // 目标节点
            int weight;    // 边权重
        }
    }

    public static class AdjacencyListByVertex { // 缺点不容易描述边信息
        private Map<Integer, List<Node>> adjList;
        private int vertexCount;

        public AdjacencyListByVertex() {
            adjList = new HashMap<>();
        }

        public void addEdge(Node from, Node to) {
            adjList.getOrDefault(from.id, new LinkedList<>()).add(to);
        }

    }

    public static class AdjacencyListByEdge {
        private Map<Integer, List<Edge>> adjList;
        private int vertexCount;

        public AdjacencyListByEdge(int vertexCount) {
            this.vertexCount = vertexCount;
            adjList = new HashMap<>();
        }

        public void addEdge(Node node, Edge edge) {
            adjList.getOrDefault(node.id, new LinkedList<>()).add(edge);
        }
    }


    /**
     * 三 关联矩阵
     */
    public static class IncidenceMatrix {
        private int[][] matrix;  // 矩阵:行=顶点,列=边
        private int vertexCount;
        private int edgeCount;

        public IncidenceMatrix(int vertexCount, int edgeCount) {
            this.vertexCount = vertexCount;
            this.edgeCount = edgeCount;
            matrix = new int[vertexCount][edgeCount];
        }

        public void addEdge(Edge edge) {
            matrix[edge.from.id][edge.id] = 1;
            matrix[edge.to.id][edge.id] = 1;
        }


        public void addEdge(Node node, Edge edge) {
            matrix[node.id][edge.id] = 1;
        }
    }

    /**
     * 四 边列表
     */
    public static class EdgeList {

        private List<Edge> edges = new ArrayList<>();

        public void addEdge(Edge edge) {
            edges.add(edge);
        }

        public void addEdge(Node from, Node to) {
            edges.add(new Edge(from, to, 1));
        }

    }
}

三、图的关系建模

虽然图结构在本质上是非关系型的,但在没有图数据库(如 Neo4j、JanusGraph)可用的场景下,我们仍可以通过关系型数据库(RDBMS)来表达图的数据结构与连接关系。

3.1 建模基本思路

图在 RDBMS 中建模的核心思想是将顶点和边分别表示为关系表(Relation)

  • 顶点表(Vertex Table):存储图中的所有节点;

  • 边表(Edge Table):存储图中所有的边(连接关系)。

3.2 表结构设计

(1)顶点表设计
CREATE TABLE vertex (
    id INT PRIMARY KEY,         -- 顶点唯一标识
    label_id INT NOT NULL,      -- 顶点类型
    properties JSON,            -- 顶点属性
    FOREIGN KEY (label_id) REFERENCES label(id)
);

properties 字段可以灵活地以 JSON 形式存储各类属性,适合属性异构的图场景。如果属性固定,也可将其展开为单独的列。

(2)边表设计(适用于有向图)
CREATE TABLE edge (
    id INT PRIMARY KEY,              -- 边的唯一标识
    from_vertex INT NOT NULL,        -- 起点
    to_vertex INT NOT NULL,          -- 终点
    label_id INT NOT NULL,           -- 边的类型
    weight DECIMAL(10,2),            -- 权重
    properties JSON,                 -- 边的属性
    FOREIGN KEY (from_vertex) REFERENCES vertex(id),
    FOREIGN KEY (to_vertex) REFERENCES vertex(id),
    FOREIGN KEY (label_id) REFERENCES label(id)
);
  • 该结构适用于有向图(Directed Graph)

  • 支持为每条边添加类型、权重或其他灵活的属性;

  • 使用外键约束维护边和顶点之间的完整性。

(3)标签元数据表设计
CREATE TABLE label (
    id INT PRIMARY KEY,            -- 标签唯一标识
    label VARCHAR(100),            -- 标签名称
    category INT NOT NULL          -- 标签分类:标识顶点标签还是边标签(例如:1=顶点,2=边)
);
(4)无向图处理方法

对于无向图(Undirected Graph),可使用以下两种策略之一:

  1. 对称存储:每条边存储两次,分别是 (u → v)(v → u)

  2. 约束存储顺序:强制存储一次,并约定 from_vertex < to_vertex,查询时统一处理对称性。


1 图的基本结构相关概念

概念简要说明
顶点(Vertex)图中的节点,代表事物
边(Edge)顶点之间的连接,代表关系
度(Degree)顶点的连接数目(有向图分入度、出度)
权值(weight cost)顶点或边的附加属性
自环(Loop)一条连接自身的边,如 (v, v)
重边(Multiple Edge)多条连接同一对顶点的边
邻接(Adjacency)两顶点之间有边连接称为邻接
路径(Path)一组连接顶点的边序列
简单路径路径中无重复顶点
环(Cycle)起点和终点相同的简单路径
简单图(Simple Graph)无自环、无重边的图
多重图(Multigraph)允许重边和/或自环
子图(Subgraph)图的顶点和边的子集组成的图
完全图(Complete Graph)任意两顶点之间都存在一条边
稀疏图/稠密图边的数量相对少/多
平面图(Planar Graph)可以画在平面上不相交的图
伪图(Pseudograph)允许重边和自环的图

2图的类型和性质

概念简要说明
有向图(Directed Graph)边有方向,表示从 u → v
无向图(Undirected Graph)边无方向,(u, v) 与 (v, u) 等价
加权图(Weighted Graph)边带有权值,代表代价、距离等
非加权图边权相同或无权
连通图(Connected Graph)任意两顶点之间都有路径(无向图)
强连通图(Strongly Connected)有向图中任意两顶点可达
弱连通图有向图忽略方向后连通
二分图(Bipartite Graph)顶点集可分为两部分,边仅连接不同集合
森林(Forest)不含环的无向图
树(Tree)连通的无环无向图
DAG(有向无环图)没有有向环的有向图(常用于任务调度)
欧拉图(Eulerian Graph)存在一条路径遍历每条边恰好一次
哈密顿图(Hamiltonian Graph)存在一条路径遍历每个顶点恰好一次

3 图的遍历与算法相关概念

概念简要说明
深度优先遍历(DFS)类似树的先序遍历
广度优先遍历(BFS)层次搜索方式
拓扑排序(Topological Sort)DAG 的顶点线性排序
连通分量(Connected Component)最大的连通子图
割点(Articulation Point)删除该点会使图不连通
桥(Bridge)删除这条边会使图不连通
图染色(Graph Coloring)给图的顶点上色,使相邻点颜色不同
最小生成树(Minimum Spanning Tree)覆盖所有顶点、权值最小的树
最短路径(Shortest Path)顶点之间路径权值最小
网络流(Network Flow)求最大流/最小割等问题
匹配(Matching)二分图中点之间的配对关系
图同构(Graph Isomorphism)判断两个图是否结构一致
图压缩(Graph Compression)将多个顶点合并成一个顶点

4 图的矩阵表示与运算

概念简要说明
邻接矩阵(Adjacency Matrix)顶点对之间的连接关系
邻接表(Adjacency List)每个顶点的邻接顶点列表
关联矩阵(Incidence Matrix)顶点和边的对应关系
边列表边列表使用一个列表来记录图中所有边

5 高阶图模型与扩展概念

概念简要说明
超图(Hypergraph)一条边可以连接多个顶点
动态图(Dynamic Graph)图结构随时间变化
随机场(Random Graph)边的生成遵循概率模型(如 Erdős–Rényi)
社区结构(Community)图中顶点聚类成的子结构
小世界图(Small-world)局部聚集 + 全局稀疏的图模型
无标度图(Scale-free)顶点度数满足幂律分布
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

GawynKing

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值