【408考点之数据结构】图的存储和基本操作

图的存储和基本操作

图(Graph)是一种用于表示节点及其相互关系的数据结构,在计算机科学中有广泛的应用。图的存储和基本操作是理解和应用图数据结构的基础。以下总结了图的存储方式及其基本操作。

一、图的存储方式
  1. 邻接矩阵(Adjacency Matrix)

    邻接矩阵是一种简单且直观的图的表示方法,用一个二维数组表示顶点之间的连接关系。

    • 特点

      • 对于一个包含 (n) 个顶点的图,邻接矩阵是一个 (n \times n) 的二维数组。
      • 若图中存在边 ( (i, j) ),则矩阵元素 ( A[i][j] = 1 )(或边的权值),否则 ( A[i][j] = 0 )。
      • 对于无向图,邻接矩阵是对称的;对于有向图,矩阵不一定对称。
    • 优缺点

      • 优点:简单直观,易于实现,方便查找两顶点之间是否存在边。
      • 缺点:空间复杂度高,对于稀疏图不适用。

    代码实现

    #define MAXVEX 100
    typedef struct {
        int vexs[MAXVEX];            // 顶点表
        int arc[MAXVEX][MAXVEX];     // 邻接矩阵
        int numVertexes, numEdges;   // 图中当前的顶点数和边数
    } MGraph;
    
  2. 邻接表(Adjacency List)

    邻接表是一种链表结构,每个顶点对应一个链表,链表中的节点表示与该顶点相连的其他顶点。

    • 特点

      • 每个顶点都有一个链表,链表中的每个节点对应一个相邻顶点。
      • 边表结点存储邻接点和指向下一个邻接点的指针。
    • 优缺点

      • 优点:节省空间,适用于稀疏图。
      • 缺点:查找顶点之间的边不如邻接矩阵方便。

    代码实现

    typedef struct EdgeNode {        // 边表结点
        int adjvex;                  // 邻接点域,存储该顶点对应的下标
        int weight;                  // 用于存储权值,对于非网图可以不需要
        struct EdgeNode *next;       // 链域,指向下一个邻接点
    } EdgeNode;
    
    typedef struct VertexNode {      // 顶点表结点
        int data;                    // 顶点域,存储顶点信息
        EdgeNode *firstEdge;         // 边表头指针
    } VertexNode, AdjList[MAXVEX];
    
    typedef struct {
        AdjList adjList;
        int numVertexes, numEdges;   // 图中当前顶点数和边数
    } GraphAdjList;
    
  3. 十字链表和邻接多重表

    为了更高效地处理稀疏图,有时会使用十字链表(Orthogonal List)和邻接多重表(Adjacency Multilist)等复杂的存储结构。

    • 十字链表

      • 适用于有向图,将每条边用两个指针分别指向起点和终点顶点的链表。
    • 邻接多重表

      • 适用于无向图,解决了邻接表中边的信息冗余问题,每条边只存储一次。
二、图的基本操作
  1. 插入操作

    • 在图中插入顶点和边。

    邻接矩阵插入边

    void InsertEdge(MGraph *G, int i, int j) {
        if (i >= G->numVertexes || j >= G->numVertexes) return;
        G->arc[i][j] = 1;
        G->arc[j][i] = 1;  // 无向图
        G->numEdges++;
    }
    

    邻接表插入边

    void InsertEdge(GraphAdjList *G, int i, int j) {
        EdgeNode *e = (EdgeNode *)malloc(sizeof(EdgeNode));
        e->adjvex = j;
        e->next = G->adjList[i].firstEdge;
        G->adjList[i].firstEdge = e;
    
        e = (EdgeNode *)malloc(sizeof(EdgeNode));
        e->adjvex = i;
        e->next = G->adjList[j].firstEdge;
        G->adjList[j].firstEdge = e;
    
        G->numEdges++;
    }
    
  2. 删除操作

    • 在图中删除顶点和边。

    邻接矩阵删除边

    void DeleteEdge(MGraph *G, int i, int j) {
        if (i >= G->numVertexes || j >= G->numVertexes) return;
        G->arc[i][j] = 0;
        G->arc[j][i] = 0;  // 无向图
        G->numEdges--;
    }
    

    邻接表删除边

    void DeleteEdge(GraphAdjList *G, int i, int j) {
        EdgeNode *p = G->adjList[i].firstEdge;
        EdgeNode *pre = NULL;
        while (p && p->adjvex != j) {
            pre = p;
            p = p->next;
        }
        if (p) {
            if (pre) pre->next = p->next;
            else G->adjList[i].firstEdge = p->next;
            free(p);
        }
    
        p = G->adjList[j].firstEdge;
        pre = NULL;
        while (p && p->adjvex != i) {
            pre = p;
            p = p->next;
        }
        if (p) {
            if (pre) pre->next = p->next;
            else G->adjList[j].firstEdge = p->next;
            free(p);
        }
    
        G->numEdges--;
    }
    
  3. 遍历操作

    • 遍历图的所有顶点和边。常用的遍历方法有深度优先搜索(DFS)和广度优先搜索(BFS)。

    深度优先搜索(DFS)

    void DFS(GraphAdjList *G, int i, int *visited) {
        visited[i] = 1;
        printf("%d ", G->adjList[i].data);
        EdgeNode *p = G->adjList[i].firstEdge;
        while (p) {
            if (!visited[p->adjvex]) {
                DFS(G, p->adjvex, visited);
            }
            p = p->next;
        }
    }
    
    void DFSTraverse(GraphAdjList *G) {
        int visited[MAXVEX];
        for (int i = 0; i < G->numVertexes; i++) visited[i] = 0;
        for (int i = 0; i < G->numVertexes; i++) {
            if (!visited[i]) DFS(G, i, visited);
        }
    }
    

    广度优先搜索(BFS)

    void BFS(GraphAdjList *G, int i, int *visited) {
        int queue[MAXVEX], front = 0, rear = 0;
        visited[i] = 1;
        printf("%d ", G->adjList[i].data);
        queue[rear++] = i;
        while (front != rear) {
            int u = queue[front++];
            EdgeNode *p = G->adjList[u].firstEdge;
            while (p) {
                if (!visited[p->adjvex]) {
                    visited[p->adjvex] = 1;
                    printf("%d ", G->adjList[p->adjvex].data);
                    queue[rear++] = p->adjvex;
                }
                p = p->next;
            }
        }
    }
    
    void BFSTraverse(GraphAdjList *G) {
        int visited[MAXVEX];
        for (int i = 0; i < G->numVertexes; i++) visited[i] = 0;
        for (int i = 0; i < G->numVertexes; i++) {
            if (!visited[i]) BFS(G, i, visited);
        }
    }
    

通过以上图的存储方式及其基本操作的理解和实现,可以更好地掌握图这一数据结构的应用和优化。在实际应用中,根据具体问题选择合适的存储方式和操作方法,能够有效提高算法的效率和性能。

  • 53
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值