图的基本知识及实现_邻接矩阵_邻接表

对于有 nn 个顶点的图 G = (V, E)来说,我们可以用一个 n * n的矩阵 AA来表示 G中各顶点的相邻关系,如果 v_{i}和 v_{j}之间存在边(或弧),则
A[i][j] = 1,
否则 A[i][j] = 0。
下图为有向图 G_{1}G1
​​ 以及对应的邻接矩阵。

这里写图片描述这里写图片描述

下图为无向图 G_{2}G2
​​ 以及对应的邻接矩阵。

这里写图片描述这里写图片描述

图的邻接矩阵是唯一的,矩阵的大小只与顶点个数 NN 有关,是一个 N * NN∗N 的矩阵。前面我们已经介绍过,在无向图里,如果顶点 v_{i}vi
v_{j}v​j之间有边,则可认为顶点 v_{i}v​i到 v_{j}v​j有边,同时顶点 v_{j}v​j到 v_{i}v​i 也有边。对应到邻接矩阵里,
则有
A[i][j]=A[j][i]=1。
因此我们可以发现,无向图的邻接矩阵是一个对称矩阵。

在邻接矩阵上,我们可以直观的看出两个顶点之间是否有边(或弧),并能容易求出每个顶点的度,入度和出度。

这里我们以图 G_{1}G​1为例,演示下如何利用邻接矩阵计算顶点的入度和出度。顶点的出度,即为邻接矩阵上点对应行上所有值的总和,比如顶点 1 对应的出度即为
0+1+1+1=3;而每个点的入度即为点对应列上所有值的总和,比如顶点 3 对应的入度即为
1+0+0+1=2。

这里写图片描述这里写图片描述

我们再来学习邻接表

邻接表是图的一种顺序存储与链式存储相结合的存储方法。我们给图 GG 中的每个顶点建立一个单链表,第 ii 个单链表中的结点表示依附于顶点 v_{i}​​ 的边(对于有向图是以 v_{i}为起点的弧)。所有单链表的表头结点都存储在一个一维数组中,以便于顶点的访问。下图为图 G_{1}对应的邻接表。

这里写图片描述
这里写图片描述

在无向图的邻接表中,顶点 v_{i}的度为第 i个单链表中的结点数;而在有向图中,第 ii 个单链表中的结点数表示的是顶点 v_{i}v
​i的出度,如果要求入度,则要遍历整个邻接表。另外,在邻接表中,我们很容易就能知道某一顶点和哪些顶点相连接。

学习完两种存储结构,可能你会有这样的疑问:那我们什么时候用邻接矩阵,什么时候用邻接表呢?

我们可以看到,邻接矩阵存储结构最大的优点就是简单直观,易于理解和实现。其适用范围广泛,有向图、无向图、混合图、带权图等都可以直接用邻接矩阵表示。另外,对于很多操作,比如获取顶点度数,判断某两点之间是否有连边等,都可以在常数时间内完成。

然而,它的缺点也是显而易见的:从以上的例子我们可以看出,对于一个有 n 个顶点的图,邻接矩阵总是需要n​2的存储空间。当边数很少的时候,就会造成空间的浪费。

因此,具体使用哪一种存储方式,要根据图的特点来决定:如果是稀疏图,我们一般用邻接表来存储,这样可以节省空间;如果是稠密图,考虑到邻接表中要附加链域,我们一般用邻接矩阵来存储

邻接表的创建

#include <iostream>
#include <cstring>
using namespace std;

class Graph {
private:
    int **mat;
    int n;

public:
    Graph(int input_n) {
        n = input_n;
        mat = new int*[n];
        for (int i = 0; i < n; ++i) {
            mat[i] = new int[n];
            memset(mat[i], 0, sizeof(int) * n);
        }
    }

    ~Graph() {
        for (int i = 0; i< n; ++i) {
            delete[] mat[i];
        }
        delete[] mat;
    }

    void insert(int x, int y) {
        mat[x][y]=1;
    }

    void output() {
        for(int i=0;i<n;i++){
            for(int j=0;j<n;j++){
                cout<<mat[i][j]<<" ";
            }
            cout<<endl;
        }

    }
};

int main() {
    int n, m, x, y;
    cin >> n >> m;
    Graph g(n);
    for (int i = 0; i < m; ++i) {
        cin >> x >> y;
        g.insert(x, y);
    }
    g.output();
    return 0;
}

邻接表

#include <iostream>
using namespace std;

class LinkedListNode {
public:
    int vertex;
    LinkedListNode *next;

    LinkedListNode(int vertex_input) {
        vertex = vertex_input;
        next = NULL;
    }
};

class LinkedList {
public:
    LinkedListNode *head;

    LinkedList() {
        head = NULL;
    }

    ~LinkedList() {
        while (head != NULL) {
            LinkedListNode *delete_node = head;
            head = head->next;
            delete delete_node;
        }
    }

    void insert(int vertex) {
        LinkedListNode *node = new LinkedListNode(vertex);
        node->next = head;
        head = node;
    }
};

class Graph {
private:
    LinkedList *edges;
    int n;
public:
    Graph(int input_n) {
        n = input_n;
        edges = new LinkedList[n];
    }

    ~Graph() {
        delete[] edges;
    }

    void insert(int x, int y) {
        edges[x].insert(y);
    }

    void output() {
        for(int i=0;i<n;i++){
            cout<<i<<":";
            for(auto j=edges[i].head;j!=NULL;j=j->next){
                cout<<j->vertex<<" ";
            }
            cout<<endl;
        }
    }
};

int main() {
    int n, m, x, y;
    cin >> n >> m;
    Graph g(n);
    for (int i = 0; i < m; ++i) {
        cin >> x >> y;
        g.insert(x, y);
    }
    g.output();
    return 0;
}
  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值