邻接矩阵图Graph——数据结构系列教程(c++版)

梦想不会自己发光,真正闪耀的是那个为梦狂奔的你。献给知行的孩子们!(Eric.He著)


  图(Graph)是计算机科学中最核心的非线性数据结构之一,用于描述多对多的复杂关系,相比线性表(一对一)、树(一对多),图(多对多)能更真实地模拟现实世界中的关联场景(如社交网络、交通路网、电路拓扑等)。本教程基于数组实现邻接矩阵图,从核心原理、代码结构到实战使用,全面讲解邻接矩阵图的原理与实现,功能包含创建图、添加顶点、添加边、连通测试、最短路径、打印邻接链表、深度优先遍历(DFS)、广度优先遍历(BFS) 等核心功能。

教程目录导航

一、图核心概念

1.1 图的定义

图是由两个有限集合构成的二元组 G = (V, E):

  1. 顶点集(Vertex Set, V):非空有限集合,每个元素称为顶点(Vertex/Node),是图的基本单元(如地图中的城市、社交网络中的用户);
  2. 边集(Edge Set, E):有限集合,每个元素称为边(Edge/Arc),用于连接两个顶点,描述顶点间的关系(如城市间的道路、用户间的好友关系)。

演示动画

1.2 按边的方向分类

  1. 无向图(Undirected Graph):边没有方向,若顶点 v 和 w 间有边,则记为 (v, w),且 (v, w) = (w, v)。
    • 示例:双向好友关系、无向道路(可双向通行);
    • 无向边的度数:一个顶点的度数 = 与该顶点相连的边数(如顶点 0 连接 2、4,则 0 的度数为 2)。
  2. 有向图(Directed Graph/Digraph):边有方向,若从顶点 v 指向 w 的边,记为 <v, w>,且 <v, w> ≠ <w, v>。
    • 示例:单向关注、微信转账(有明确的转出 / 转入方向);
    • 有向边的度数

      入度(In-Degree):指向该顶点的边数(如有向图中顶点 2 指向它的有 0、5,6 则 2 的入度数为 3);

      出度(Out-Degree):从该顶点出发的边数(如有向图中顶点 2 指向 0、5 则 2 的出度数为 2);

      总度数 = 入度 + 出度。

1.3 按边的权重分类

  1. 无权图(Unweighted Graph):边仅表示 “是否相连”,无额外数值属性。
    • 示例:普通社交关系(仅标记是否为好友)。
  2. 带权图(Weighted Graph/Network):边附带一个数值(权重 / 权值,Weight),表示连接的 “代价” 或 “收益”。
    • 示例:交通路网中边的权重为距离 / 时间、电商平台中用户间的互动强度。

1.4 按连通性分类

  1. 连通图(Undirected Connected Graph):无向图中,任意两个顶点间都存在至少一条路径可达。
    • 非连通图:无向图中存在至少两个顶点不可达,每个连通的子图称为连通分量。
  2. 强连通图(Strongly Connected Graph):有向图中,任意两个顶点 v 和 w,既存在 v→w 的路径,也存在 w→v 的路径。
    • 弱连通图:有向图忽略边的方向后是连通图;
    • 强连通分量:有向图中最大的强连通子图。

1.5 其它概念

术语 定义
路径(Path) 从顶点 v 到 w 的顶点序列,序列中相邻顶点间有边相连;
路径长度 无权图中为边的数量,带权图中为边的权重和;
环(Cycle) 起点和终点为同一个顶点的路径(如 A→B→C→A);
简单路径 路径中所有顶点不重复;
完全图 无向完全图中任意两个顶点间都有边(总边数 = n(n-1)/2,n 为顶点数);有向完全图中任意两个顶点间都有双向边(总边数 = n(n-1));
稀疏图 / 稠密图 边数远少于完全图的为稀疏图(如边数 e ≈ n),边数接近完全图的为稠密图(如 e ≈ n²)。

1.6 存储结构

图的存储核心是 “记录顶点信息” 和 “记录顶点间的边关系”,常用方式有邻接矩阵和邻接链表,二者各有优劣。

(1)邻接矩阵(Adjacency Matrix)

邻接矩阵的核心思想是「一维数组 + 二维数组」

顶点数组:用一维数组存储所有顶点的基本信息(如顶点名称vertices);

邻接矩阵:用 n×n 的二维数组 graph 表示 n 个顶点的图

顶点映射:将每个顶点编号为 0~n-1,对应数组的行 / 列索引;

边的表示:

优点 缺点
判断两顶点是否有边:O (1) 空间复杂度:O (n²)(浪费)
计算顶点度数:O (n) 稀疏图中内存利用率极低
实现简单、直观 遍历邻接点:O (n)

(2)邻接链表(Adjacency List)

邻接链表的核心思想是「数组 + 链表」

顶点数组:用一维数组存储所有顶点节点的基本信息(如顶点名称data、第一个邻接边节点地址firstedge);

邻接链表:每个顶点对应一个单向链表,存储该顶点的所有邻接点及边的权重。每一个节点有三个域,即顶点索引adjvex、weight边的权值、next下一个边节点。

优点 缺点
空间复杂度:O (n+e)(高效) 判断两顶点是否有边:O (k)(k 为邻接点数量)
稀疏图中内存利用率极高 实现稍复杂
遍历邻接点:O (k)(k 为邻接点数量) 稠密图中效率不如邻接矩阵

二、代码结构解析

2.1 整体架构

代码分为2个核心部分:

模块 作用 关键变量/结构/函数
全局常量
  • 最大顶点数(可根据需求调整)
  • 表示无边
  • 无边/无穷大标记
  • 路径前驱的无效标记
  • #define MAX_VERTEX 100
  • #define NO_EDGE 0
  • #define INF INT_MAX
  • #define NO_PRE -1
邻接矩阵图结构体 封装所有操作 ArrayGraph

2.2 关键结构体详解

(1)全局常量

            
#define MAX_VERTEX 100  // 最大顶点数(可根据需求调整)
#define NO_EDGE 0       // 表示无边
#define INF INT_MAX     // 无边/无穷大标记
#define NO_PRE -1       // 路径前驱的无效标记
            
        

(2)邻接矩阵图结构体(ArrayGraph)

分为私有成员(内部实现)和公有成员(对外接口):


struct ArrayGraph
{
    //------------------------------------------------------------------------------------------------------
    //                                          私有成员                                                ||
    // 注:私有成员只能在该结构体内部访问调用,外部通过该结构体定义(实例化)的变量(对象)不能访问调用)   ||
    //------------------------------------------------------------------------------------------------------
    private:

        //---------------声明私有成员变量---------------        

        //---------------声明私有成员函数---------------
        
        int findMinDist(int dist[], bool visited[], int n);//找到未访问的距离最小的顶点  dist 距离数组  visited 访问标记数组  n 顶点数 距离最小的顶点索引,无则返回-1
        void printPath(int pre[], int startIndex, int targetIndex);//打印路径(递归回溯前驱数组)  pre 前驱数组  startIndex 起始顶点索引  targetIndex 目标顶点索引
        int dfsCheckConnect(int startIndex, int targetIndex, bool visited[]);//DFS辅助函数:判断从startIndex能否到达targetIndex visited 访问标记数组 能到达返回1,否则返回0
        void dijkstra(int startIndex, int dist[], int pre[]);//Dijkstra算法:计算起始顶点到所有顶点的最短路径 startIndex 起始顶点索引  dist 输出参数:存储起始顶点到各顶点的最短距离  pre 输出参数:存储各顶点的路径前驱索引
        void dfsHelper(int index, bool visited[]);//DFS辅助函数:index 当前顶点索引 visited 访问标记数组


    //------------------------------------------------------------------------------------------------------
    //                                          公共成员                                                ||
    // 注:公共成员能在该结构体内部访问调用,外部通过该结构体定义(实例化)的变量(对象)也能访问调用)     ||
    //------------------------------------------------------------------------------------------------------
    public:

        //---------------声明公有成员变量---------------      
        char vertices[MAX_VERTEX];          // 顶点集合(存储顶点名称,如'A'、'B')
        int graph[MAX_VERTEX][MAX_VERTEX];  // 邻接矩阵
        int vertexNum;                      // 实际顶点数
        int edgeNum;                        // 实际边数

        //---------------声明公共成员函数---------------

        // 构造函数:外部通过该结构体定义(实例化)变量(对象)时,自动执行该函数(主要用于初始化成员变量的值)
        ArrayGraph() {
            vertexNum = edgeNum = 0;
             // 初始化邻接矩阵为NO_EDGE(无边)
             for (int i = 0; i < MAX_VERTEX; i++) {
                 for (int j = 0; j < MAX_VERTEX; j++) {
                    graph[i][j] = NO_EDGE;
                 }
             }
        }

        int findVertexIndex(char vertex);//查找顶点的索引,未找到返回-1
        int addVertex(char vertex);//添加顶点 vertex 顶点名称(如'A')
        int addEdge(char v1, char v2, int weight);//添加边(无向图)v1 顶点1 v2 顶点2 weight 边的权值(需>0,NO_EDGE=0表示无边)
        int addDirectedEdge(char v1, char v2, int weight);//添加边(有向图)v1 顶点1 v2 顶点2 weight 边的权值(需>0,NO_EDGE=0表示无边)
        void printAdjacency();//打印邻接矩阵
        int isConnected(char v1, char v2);//判断两个顶点是否连通 v1 顶点1 v2 顶点2 连通返回1,不连通返回0,顶点不存在返回-1
        int getShortestPath(char start, char target);//获取两顶点的最短路径(封装Dijkstra) start 起始顶点  target 目标顶点  最短路径长度(INF表示无路径,-1表示顶点不存在)
        void dfs(char start);//深度优先遍历(DFS) start 起始顶点
        void bfs(char start);//广度优先遍历(BFS)start 起始顶点

        // 析构函数:自动销毁邻接矩阵图,避免内存泄漏
        ~ArrayGraph() {
        }
};

三、核心操作实现详解

3.1 添加顶点(addVertex)

添加顶点 vertex 顶点名称(如'A')


int ArrayGraph::addVertex(char vertex)
{
    if (vertexNum >= MAX_VERTEX) {
        printf("顶点数已达上限,无法添加!\n");
        return 0;
    }
    vertices[vertexNum++] = vertex;
    return 1;
}

3.2 添加边

v1 顶点1 v2 顶点2 weight 边的权值(需>0,NO_EDGE=0表示无边)

(1)无向图(addEdge)


int ArrayGraph::addEdge(char v1, char v2, int weight)
{
    int i = findVertexIndex(v1);
    int j = findVertexIndex(v2);

    if (i == -1 || j == -1) {
        return 0;
    }

    // 无向图:双向置weight
    graph[i][j] = weight;
    graph[j][i] = weight;
    edgeNum++;
    return 1;
}

(2)有向图(addDirectedEdge)


int ArrayGraph::addDirectedEdge(char v1, char v2, int weight)
{
    int i = findVertexIndex(v1);
    int j = findVertexIndex(v2);

    if (i == -1 || j == -1) {
        return 0;
    }

    // 有向图:单向置weight
    graph[i][j] = weight;
    edgeNum++;
    return 1;
}
查找顶点的索引(辅助函数)

int ArrayGraph::findVertexIndex(char vertex)
{
    for (int i = 0; i < vertexNum; i++) {
       if (vertices[i] == vertex) {
           return i;
       }
    }
    printf("顶点%c不存在!\n", vertex);
    return -1;
}

3.3 深度优先遍历(DFS)(dfs)


void ArrayGraph::dfs(char start)
{
    int startIndex = findVertexIndex(start);
    if (startIndex == -1) {
        return;
    }

    // 初始化访问标记数组
    bool visited[MAX_VERTEX] = {false};
    printf("\n===== 深度优先遍历(起始:%c)=====\n", start);
    dfsHelper(startIndex, visited);
    printf("\n");
}

DFS辅助函数


void ArrayGraph::dfsHelper(int index, bool visited[])
{
    // 标记当前顶点为已访问,并打印
    visited[index] = true;
    printf("%c ", vertices[index]);

    // 遍历所有邻接顶点
    for (int j = 0; j < vertexNum; j++) {
        // 存在边且未访问
        if (graph[index][j] > NO_EDGE && !visited[j]) {
            dfsHelper(j, visited);
        }
    }
}

3.4 广度优先遍历(BFS)(bfs)


void ArrayGraph::bfs(char start)
{
    int startIndex = findVertexIndex(start);
    if (startIndex == -1) {
        return;
    }

    // 初始化访问标记数组和队列
    bool visited[MAX_VERTEX] = {false};
    int queue[MAX_VERTEX]; // 简单队列(数组实现)
    int front = 0, rear = 0; // 队列头尾指针

    printf("\n===== 广度优先遍历(起始:%c)=====\n", start);

    // 起始顶点入队并标记访问
    visited[startIndex] = true;
    queue[rear++] = startIndex;

    // 队列不为空时循环
    while (front < rear) {
        // 出队并打印
        int currIndex = queue[front++];
        printf("%c ", vertices[currIndex]);

        // 遍历所有邻接顶点
        for (int j = 0; j < vertexNum; j++) {
            if (graph[currIndex][j] > NO_EDGE && !visited[j]) {
                visited[j] = true;
                queue[rear++] = j;
            }
        }
    }
    printf("\n");
}

四、实战使用示例

4.1 使用示例

#include "ArrayGraph.h"
#include <iostream>
#include <stdio.h>

int main() {
    //  A ── B
    //  │    │
    //  C ── D ── E
    printf("\n===== 图 =====\n");
    printf("  A ── B\n");
    printf("  │    │\n");
    printf("  C ── D ── E\n");

    // 1. 创建图对象
    ArrayGraph graph;

    // 2. 添加顶点(A、B、C、D、E)
    graph.addVertex('A');
    graph.addVertex('B');
    graph.addVertex('C');
    graph.addVertex('D');
    graph.addVertex('E');

    // 3. 添加边(有向图)
    graph.addDirectedEdge('A','B',5);
    graph.addDirectedEdge('A','C',3);
    graph.addDirectedEdge('B','D',2);
    graph.addDirectedEdge('C','D',4);
    graph.addDirectedEdge('D','E',1);

    // 3. 打印邻接矩阵
    graph.printAdjacency();

    // 4. 测试连通性函数
    printf("\n===== 顶点连通性测试 =====\n");
    char v1, v2;

    // 测试1:A和E(连通)
    v1 = 'A'; v2 = 'E';
    int res1 = graph.isConnected(v1, v2);
    if (res1 == 1) {
        printf("顶点%c和%c:连通\n", v1, v2);
    } else if (res1 == 0) {
        printf("顶点%c和%c:不连通\n", v1, v2);
    } else {
        printf("顶点%c或%c不存在\n", v1, v2);
    }

    // 5. 测试最短路径(A -> E)
    graph.getShortestPath('A', 'E'); // A→C→D→E,长度3+4+1=8

    // 6. 深度优先遍历(起始:A)
    graph.dfs( 'A');

    // 7 广度优先遍历(起始:A)
    graph.bfs('A');

    return 0;
}

输出结果


  A ── B
  │    │
  C ── D ── E

  ===== 带权邻接矩阵 =====
      A   B   C   D   E
  A   0   5   3   0   0
  B   5   0   0   2   0
  C   3   0   0   4   0
  D   0   2   4   0   1
  E   0   0   0   1   0

  ===== 顶点连通性测试 =====
  顶点A和E:连通

  ===== 最短路径(A -> E) =====
  路径:A -> B -> D -> E
  最短路径长度:3

  ===== 深度优先遍历(起始:A)=====
  A B D C E

  ===== 广度优先遍历(起始:A)=====
  A B C D E

===== 图内存已全部释放 =====

五、完整可运行代码

ArrayGraph.h 头文件


#ifndef ARRAYGRAPH_H_INCLUDED
#define ARRAYGRAPH_H_INCLUDED

//************************************************************************************************************************************************************************
//
//      类名:邻接矩阵图(ArrayGraph),全称:Adjacency Matrix Graph
//
//      概述:一个可复用的数组邻接矩阵图,存储结构采用二维数组实现。
//
//      邻接矩阵:分为V和E集合,其中,V是顶点,E是边。因此,用一个一维数组存放图中所有顶点数据;用一个二维数组存放顶点间关系(边或弧)的数据,这个二维数组称为邻接矩阵。
//
//      功能:包含创建图、添加顶点、添加边、连通测试、最短路径、打印邻接矩阵、深度优先遍历(DFS)、广度优先遍历(BFS) 等核心功能。
//
//      说明:
//
//************************************************************************************************************************************************************************

#define MAX_VERTEX 100  // 最大顶点数(可根据需求调整)
#define NO_EDGE 0       //表示无边
#define INF INT_MAX     // 无边/无穷大标记
#define NO_PRE -1       // 路径前驱的无效标记

//========================================================================================================================================================================
//
//                                                              邻接矩阵图结构体(ArrayGraph)
//
//========================================================================================================================================================================
struct ArrayGraph
{
    //------------------------------------------------------------------------------------------------------
    //                                          私有成员                                                ||
    // 注:私有成员只能在该结构体内部访问调用,外部通过该结构体定义(实体化)的变量(对象)不能访问调用)   ||
    //------------------------------------------------------------------------------------------------------
    private:

        //---------------声明私有成员变量---------------

        //---------------声明私有成员函数---------------
        
        int findMinDist(int dist[], bool visited[], int n);//找到未访问的距离最小的顶点  dist 距离数组  visited 访问标记数组  n 顶点数 距离最小的顶点索引,无则返回-1
        void printPath(int pre[], int startIndex, int targetIndex);//打印路径(递归回溯前驱数组)  pre 前驱数组  startIndex 起始顶点索引  targetIndex 目标顶点索引
        int dfsCheckConnect(int startIndex, int targetIndex, bool visited[]);//DFS辅助函数:判断从startIndex能否到达targetIndex visited 访问标记数组 能到达返回1,否则返回0
        void dijkstra(int startIndex, int dist[], int pre[]);//Dijkstra算法:计算起始顶点到所有顶点的最短路径 startIndex 起始顶点索引  dist 输出参数:存储起始顶点到各顶点的最短距离  pre 输出参数:存储各顶点的路径前驱索引
        void dfsHelper(int index, bool visited[]);//DFS辅助函数:index 当前顶点索引 visited 访问标记数组


    //------------------------------------------------------------------------------------------------------
    //                                          公共成员                                                ||
    // 注:公共成员能在该结构体内部访问调用,外部通过该结构体定义(实体化)的变量(对象)也能访问调用)     ||
    //------------------------------------------------------------------------------------------------------
    public:

        //---------------声明公共成员变量---------------

        char vertices[MAX_VERTEX];          // 顶点集合(存储顶点名称,如'A'、'B')
        int graph[MAX_VERTEX][MAX_VERTEX];  // 邻接矩阵
        int vertexNum;                      // 实际顶点数
        int edgeNum;                        // 实际边数

        //---------------声明公共成员函数---------------

        // 构造函数:外部通过该结构体定义(实例化)变量(对象)时,自动执行该函数(主要用于初始化成员变量的值)
        ArrayGraph() {
            vertexNum = edgeNum = 0;
             // 初始化邻接矩阵为NO_EDGE(无边)
             for (int i = 0; i < MAX_VERTEX; i++) {
                 for (int j = 0; j < MAX_VERTEX; j++) {
                    graph[i][j] = NO_EDGE;
                 }
             }
        }

        int findVertexIndex(char vertex);//查找顶点的索引,未找到返回-1
        int addVertex(char vertex);//添加顶点 vertex 顶点名称(如'A')
        int addEdge(char v1, char v2, int weight);//添加边(无向图)v1 顶点1 v2 顶点2 weight 边的权值(需>0,NO_EDGE=0表示无边)
        int addDirectedEdge(char v1, char v2, int weight);//添加边(有向图)v1 顶点1 v2 顶点2 weight 边的权值(需>0,NO_EDGE=0表示无边)
        void printAdjacency();//打印邻接矩阵
        int isConnected(char v1, char v2);//判断两个顶点是否连通 v1 顶点1 v2 顶点2 连通返回1,不连通返回0,顶点不存在返回-1
        int getShortestPath(char start, char target);//获取两顶点的最短路径(封装Dijkstra) start 起始顶点  target 目标顶点  最短路径长度(INF表示无路径,-1表示顶点不存在)
        void dfs(char start);//深度优先遍历(DFS) start 起始顶点
        void bfs(char start);//广度优先遍历(BFS)start 起始顶点

        // 析构函数:自动销毁邻接矩阵图,避免内存泄漏
        ~ArrayGraph() {
        }
};

#endif // ARRAYGRAPH_H_INCLUDED
                

ArrayGraph.cpp 程序代码


#include  <stdio.h>
#include  <stdlib.h>
#include  <stdbool.h>
#include "ArrayGraph.h"
//**********************实现私有成员函数**********************

//找到未访问的距离最小的顶点  dist 距离数组  visited 访问标记数组  n 顶点数 距离最小的顶点索引,无则返回-1
int ArrayGraph::findMinDist(int dist[], bool visited[], int n) {
    int min = INF;
    int minIndex = -1;

    for (int i = 0; i < n; i++) {
        if (!visited[i] && dist[i] < min) {
            min = dist[i];
            minIndex = i;
        }
    }
    return minIndex;
}

//打印路径(递归回溯前驱数组)  pre 前驱数组  startIndex 起始顶点索引  targetIndex 目标顶点索引
void ArrayGraph::printPath(int pre[], int startIndex, int targetIndex) {
    if (targetIndex == startIndex) {
        printf("%c", vertices[targetIndex]);
        return;
    }
    if (pre[targetIndex] == NO_PRE) {
        printf("无路径");
        return;
    }
    // 递归打印前驱路径
    printPath( pre, startIndex, pre[targetIndex]);
    printf(" -> %c", vertices[targetIndex]);
}

//DFS辅助函数:判断从startIndex能否到达targetIndex visited 访问标记数组 能到达返回1,否则返回0
int ArrayGraph::dfsCheckConnect(int startIndex, int targetIndex, bool visited[])
{
    // 找到目标顶点,返回连通
    if (startIndex == targetIndex) {
        return 1;
    }

    visited[startIndex] = true;

    // 遍历所有邻接顶点
    for (int j = 0; j < vertexNum; j++) {
        // 有边且未访问
        if (graph[startIndex][j] != NO_EDGE && !visited[j]) {
            // 递归检查邻接顶点是否能到达目标
            if (dfsCheckConnect(j, targetIndex, visited)) {
                return 1;
            }
        }
    }

    // 所有邻接顶点都遍历完仍未找到,返回不连通
    return 0;
}

//Dijkstra算法:计算起始顶点到所有顶点的最短路径 startIndex 起始顶点索引  dist 输出参数:存储起始顶点到各顶点的最短距离  pre 输出参数:存储各顶点的路径前驱索引
void ArrayGraph::dijkstra(int startIndex, int dist[], int pre[]) {
    int n = vertexNum;
    bool sq[MAX_VERTEX] = {false};//初始化sq默认为false,false代表未确定最短路径的结点集合Q,true代表已经确定最短路径的结点集合S

    // 1. 初始化距离和前驱数组
    for (int i = 0; i < n; i++) {
        if (i == startIndex) {
            dist[i] = 0;                // 起始顶点到自身距离为0
            pre[i] = NO_PRE;            // 自身无前驱
        } else if (graph[startIndex][i] != NO_EDGE) {
            dist[i] = graph[startIndex][i];  // 初始化起始顶点的直连边的dist距离为权值
            pre[i] = startIndex;             // 初始化起始顶点的直连边的pre前驱为起始顶点
        } else {
            dist[i] = INF;              // 初始化其它顶点的dist距离为无穷大(INF)
            pre[i] = NO_PRE;            // 初始化其它顶点的pre前驱为无前驱(NO_PRE)
        }
    }
    sq[startIndex] = true;

    // 2. 依次计算每个节点的距离和前驱
    for (int i = 1; i < n; i++) {
        // 从Q中找出一个从起点到该结点代价最小的结点u
        int u = 0;
        int min = INF;
        for (int i = 0; i < n; i++) {
            if (!sq[i] && dist[i] < min) {
                min = dist[i];
                u = i;
            }
        }
        if (u == -1) break; // 所有可达顶点已处理
        sq[u] = true;// 将u从Q中移出,并放入S中

        // 对u的每一个相邻结点v进行松驰操作
        for (int v = 0; v < n; v++) {
            if (!sq[v] && graph[u][v] != NO_EDGE && dist[u] != INF) {
                if (dist[u] + graph[u][v] < dist[v]) {
                    dist[v] = dist[u] + graph[u][v];//更新距离
                    pre[v] = u; // 更新前驱
                }
            }
        }
    }
}

//深度优先遍历(DFS)index 当前顶点索引 visited 访问标记数组
void ArrayGraph::dfsHelper(int index, bool visited[])
{
    // 标记当前顶点为已访问,并打印
    visited[index] = true;
    printf("%c ", vertices[index]);

    // 遍历所有邻接顶点
    for (int j = 0; j < vertexNum; j++) {
        // 存在边且未访问
        if (graph[index][j] > NO_EDGE && !visited[j]) {
            dfsHelper(j, visited);
        }
    }
}


//**********************实现公有成员函数**********************

//查找顶点的索引,未找到返回-1
int ArrayGraph::findVertexIndex(char vertex)
{
    for (int i = 0; i < vertexNum; i++) {
       if (vertices[i] == vertex) {
           return i;
       }
    }
    printf("顶点%c不存在!\n", vertex);
    return -1;
}

  //添加顶点 vertex 顶点名称(如'A')
int ArrayGraph::addVertex(char vertex)
{
    if (vertexNum >= MAX_VERTEX) {
        printf("顶点数已达上限,无法添加!\n");
        return 0;
    }
    vertices[vertexNum++] = vertex;
    return 1;
}

//添加边(无向图)v1 顶点1 v2 顶点2 weight 边的权值(需>0,NO_EDGE=0表示无边)
int ArrayGraph::addEdge(char v1, char v2, int weight)
{
    int i = findVertexIndex(v1);
    int j = findVertexIndex(v2);

    if (i == -1 || j == -1) {
        return 0;
    }

    // 无向图:双向置weight
    graph[i][j] = weight;
    graph[j][i] = weight;
    edgeNum++;
    return 1;
}

//添加边(有向图)v1 顶点1 v2 顶点2 weight 边的权值(需>0,NO_EDGE=0表示无边)
int ArrayGraph::addDirectedEdge(char v1, char v2, int weight)
{
    int i = findVertexIndex(v1);
    int j = findVertexIndex(v2);

    if (i == -1 || j == -1) {
        return 0;
    }

    // 有向图:单向置weight
    graph[i][j] = weight;
    edgeNum++;
    return 1;
}

//打印邻接矩阵
void ArrayGraph::printAdjacency()
{
    printf("\n===== 带权邻接矩阵 =====\n");
    // 打印顶点名称(表头)
    printf("   "); // 对齐
    for (int i = 0; i < vertexNum; i++) {
        printf("%3c", vertices[i]);
    }
    printf("\n");

    // 打印邻接矩阵内容
    for (int i = 0; i < vertexNum; i++) {
        printf("%3c", vertices[i]);
        for (int j = 0; j < vertexNum; j++) {
            // 无边显示0,有边显示权值
            printf("%3d", graph[i][j]);
        }
        printf("\n");
    }
    printf("========================\n");
}

//判断两个顶点是否连通 v1 顶点1 v2 顶点2 连通返回1,不连通返回0,顶点不存在返回-1
int ArrayGraph::isConnected(char v1, char v2)
{
    // 1. 查找顶点索引
    int i = findVertexIndex(v1);
    int j = findVertexIndex(v2);

    // 顶点不存在
    if (i == -1 || j == -1) {
        return -1;
    }

    // 2. 同一个顶点,直接连通
    if (i == j) {
        return 1;
    }

    // 3. 初始化访问标记数组
    bool visited[MAX_VERTEX] = {false};

    // 4. 通过DFS检查连通性
    return dfsCheckConnect(i, j, visited);
}

 //获取两顶点的最短路径(封装Dijkstra) start 起始顶点  target 目标顶点  最短路径长度(INF表示无路径,-1表示顶点不存在)
int ArrayGraph::getShortestPath(char start, char target) {
    // 1. 检查顶点合法性
    int startIndex = findVertexIndex(start);
    int targetIndex = findVertexIndex(target);
    if (startIndex == -1 || targetIndex == -1) {
        return -1;
    }

    // 2. 同一顶点,路径长度为0
    if (startIndex == targetIndex) {
        printf("\n===== 最短路径(%c → %c)=====\n", start, target);
        printf("路径:%c\n", start);
        printf("最短路径长度:0\n");
        return 0;
    }

    // 3. 初始化距离和前驱数组
    int dist[MAX_VERTEX];
    int pre[MAX_VERTEX];
    dijkstra(startIndex, dist, pre);

    // 4. 输出结果
    printf("\n===== 最短路径(%c → %c)=====\n", start, target);
    if (dist[targetIndex] == INF) {
        printf("路径:无\n");
        printf("最短路径长度:∞(无连通路径)\n");
        return INF;
    } else {
        printf("路径:");
        printPath(pre, startIndex, targetIndex);
        printf("\n最短路径长度:%d\n", dist[targetIndex]);
        return dist[targetIndex];
    }
}

//深度优先遍历(DFS) start 起始顶点
void ArrayGraph::dfs(char start)
{
    int startIndex = findVertexIndex(start);
    if (startIndex == -1) {
        return;
    }

    // 初始化访问标记数组
    bool visited[MAX_VERTEX] = {false};
    printf("\n===== 深度优先遍历(起始:%c)=====\n", start);
    dfsHelper(startIndex, visited);
    printf("\n");
}

//广度优先遍历(BFS)start 起始顶点
void ArrayGraph::bfs(char start)
{
    int startIndex = findVertexIndex(start);
    if (startIndex == -1) {
        return;
    }

    // 初始化访问标记数组和队列
    bool visited[MAX_VERTEX] = {false};
    int queue[MAX_VERTEX]; // 简单队列(数组实现)
    int front = 0, rear = 0; // 队列头尾指针

    printf("\n===== 广度优先遍历(起始:%c)=====\n", start);

    // 起始顶点入队并标记访问
    visited[startIndex] = true;
    queue[rear++] = startIndex;

    // 队列不为空时循环
    while (front < rear) {
        // 出队并打印
        int currIndex = queue[front++];
        printf("%c ", vertices[currIndex]);

        // 遍历所有邻接顶点
        for (int j = 0; j < vertexNum; j++) {
            if (graph[currIndex][j] > NO_EDGE && !visited[j]) {
                visited[j] = true;
                queue[rear++] = j;
            }
        }
    }
    printf("\n");
}

main.cpp 测试代码

#include "ArrayGraph.h"
#include <iostream>
#include <string>

int main() {
    //  A ── B
    //  │    │
    //  C ── D ── E
    printf("\n===== 图 =====\n");
    printf("  A ── B\n");
    printf("  │    │\n");
    printf("  C ── D ── E\n");

    //使用MyLibrary函数库中的ArrayGraph(邻接矩阵图结构体)定义graph变量
    ArrayGraph graph;

    //使用MyLibrary函数库中的LinkedGraph(邻接链表图结构体)定义graph变量
    //LinkedGraph graph;

    // 1. 添加顶点(A、B、C、D、E)
    graph.addVertex('A');
    graph.addVertex('B');
    graph.addVertex('C');
    graph.addVertex('D');
    graph.addVertex('E');

    // 2. 添加边(无向图)
    graph.addEdge('A','B',1);
    graph.addEdge('A','C',1);
    graph.addEdge('B','D',1);
    graph.addEdge('C','D',1);
    graph.addEdge('D','E',1);

    // 2. 添加边(有向图)
    //graph.addDirectedEdge('A','B',5);
    //graph.addDirectedEdge('A','C',3);
    //graph.addDirectedEdge('B','D',2);
    //graph.addDirectedEdge('C','D',4);
    //graph.addDirectedEdge('D','E',1);

    // 3. 打印邻接矩阵
    graph.printAdjacency();

    // 4. 测试连通性函数
    printf("\n===== 顶点连通性测试 =====\n");
    char v1, v2;

    // 测试1:A和B(连通)
    v1 = 'A'; v2 = 'E';
    int res1 = graph.isConnected(v1, v2);
    if (res1 == 1) {
        printf("顶点%c和%c:连通\n", v1, v2);
    } else if (res1 == 0) {
        printf("顶点%c和%c:不连通\n", v1, v2);
    } else {
        printf("顶点%c或%c不存在\n", v1, v2);
    }

    // 5. 测试最短路径(A -> E)
    graph.getShortestPath('A', 'E'); // A→C→D→E,长度3+4+1=8

    // 6. 深度优先遍历(起始:A)
    graph.dfs( 'A');

    // 7 广度优先遍历(起始:A)
    graph.bfs('A');

return 0;
}

输出结果


  A ── B
  │    │
  C ── D ── E

  ===== 带权邻接矩阵 =====
      A   B   C   D   E
  A   0   5   3   0   0
  B   5   0   0   2   0
  C   3   0   0   4   0
  D   0   2   4   0   1
  E   0   0   0   1   0

  ===== 顶点连通性测试 =====
  顶点A和E:连通

  ===== 最短路径(A -> E) =====
  路径:A -> B -> D -> E
  最短路径长度:3

  ===== 深度优先遍历(起始:A)=====
  A B D C E

  ===== 广度优先遍历(起始:A)=====
  A B C D E

===== 图内存已全部释放 =====

六、总结

本文详细解析了邻接矩阵图(ArrayGraph)的原理与代码实现,核心要点:

该实现可直接复用,也可根据需求扩展(如支持负权边的Bellman-Ford算法、拓扑排序等),是学习图结构和算法的优质实践案例。


返回顶部