树与图 3.4 图的表示方法(邻接矩阵与邻接表)

在计算机科学中,图是一种重要的数据结构,用于表示对象之间的关系。图由顶点(或节点)和边组成,边连接顶点。图的表示方法有多种,其中最常用的两种是邻接矩阵和邻接表。本文将详细介绍这两种表示方法,包括它们的优缺点、适用场景以及示例代码。

1. 邻接矩阵

1.1 定义

邻接矩阵是一个二维数组,用于表示图中顶点之间的连接关系。对于一个有 ( n ) 个顶点的图,邻接矩阵是一个 ( n \times n ) 的矩阵,其中矩阵的元素 ( matrix[i][j] ) 表示顶点 ( i ) 和顶点 ( j ) 之间是否存在边。

  • 对于无向图,如果存在边 ( (i, j) ),则 ( matrix[i][j] = 1 ) 和 ( matrix[j][i] = 1 )。
  • 对于有向图,如果存在边 ( (i, j) ),则 ( matrix[i][j] = 1 ),而 ( matrix[j][i] = 0 )。

1.2 示例

假设我们有一个无向图,包含 4 个顶点和 4 条边,如下所示:

    0
   / \
  1---2
   \
    3

该图的邻接矩阵表示如下:

    0  1  2  3
0 [ 0, 1, 1, 0 ]
1 [ 1, 0, 1, 1 ]
2 [ 1, 1, 0, 0 ]
3 [ 0, 1, 0, 0 ]

1.3 优点

  • 快速查询:邻接矩阵可以在 ( O(1) ) 的时间复杂度内判断任意两个顶点之间是否存在边。
  • 简单实现:实现相对简单,适合小规模图的表示。

1.4 缺点

  • 空间复杂度高:对于稀疏图,邻接矩阵会浪费大量空间,因为大部分元素可能是 0。
  • 不适合动态变化:在图的边频繁增加或删除的情况下,邻接矩阵的效率较低。

1.5 示例代码(Python)

class Graph:
    def __init__(self, vertices):
        self.V = vertices  # 顶点数量
        self.adj_matrix = [[0] * vertices for _ in range(vertices)]  # 初始化邻接矩阵

    def add_edge(self, u, v):
        self.adj_matrix[u][v] = 1  # 添加边
        self.adj_matrix[v][u] = 1  # 无向图,添加反向边

    def display(self):
        for row in self.adj_matrix:
            print(row)

# 示例
g = Graph(4)
g.add_edge(0, 1)
g.add_edge(0, 2)
g.add_edge(1, 2)
g.add_edge(1, 3)
g.display()

2. 邻接表

2.1 定义

邻接表是另一种表示图的方式,它使用一个数组(或列表)来存储每个顶点的邻接边。每个数组元素是一个链表(或其他数据结构),用于存储与该顶点相连的所有顶点。

2.2 示例

对于同样的无向图,邻接表的表示如下:

0: 1 -> 2
1: 0 -> 2 -> 3
2: 0 -> 1
3: 1

2.3 优点

  • 节省空间:对于稀疏图,邻接表比邻接矩阵更节省空间,因为它只存储实际存在的边。
  • 动态性强:在图的边频繁增加或删除的情况下,邻接表的效率较高。

2.4 缺点

  • 查询速度慢:判断任意两个顶点之间是否存在边的时间复杂度为 ( O(V) ),其中 ( V ) 是顶点的度数。
  • 实现复杂:相较于邻接矩阵,邻接表的实现稍微复杂一些。

2.5 示例代码(Python)

class Graph:
    def __init__(self, vertices):
        self.V = vertices  # 顶点数量
        self.adj_list = [[] for _ in range(vertices)]  # 初始化邻接表

    def add_edge(self, u, v):
        self.adj_list[u].append(v)  # 添加边
        self.adj_list[v].append(u)  # 无向图,添加反向边

    def display(self):
        for i in range(self.V):
            print(f"{i}: {' -> '.join(map(str, self.adj_list[i]))}")

# 示例
g = Graph(4)
g.add_edge(0, 1)
g.add_edge(0, 2)
g.add_edge(1, 2)
g.add_edge(1, 3)
g.display()

3. 总结

在选择图的表示方法时,需要根据具体的应用场景来决定。邻接矩阵适合于密集图和需要快速查询的场景,而邻接表则更适合于稀疏图和动态变化的图结构。理解这两种表示方法的优缺点,有助于在实际开发中做出更合理的选择。希望本文能为您在图的表示方法上提供清晰的指导。