C++详细讲解图论的基础与图的储存

目录
  • 一、前言
  • 二、图的定义
  • 三、图的储存
    • 1.邻接矩阵
    • 2.邻接表
    • 3.邻接矩阵与邻接表的优缺点对比

一、前言

在算法中一般都需要把问题抽象成图论问题然后用图论的算法解决问题。

图论涉及相当多的算法,包括图DFS和BFS、连通性、拓扑排序、最小生成树、最短路径、最大流网络、图的着色问题等等

图论算法在计算机科学中扮演着很重要的角色,它提供了对很多问题都有效的一种简单而系统的建模方式。很多问题都可以转化为图论问题,然后用图论的基本算法加以解决。

二、图的定义

图(graph)

如上图是一个图G,一个图由顶点(vertex)集V和边(edge)集E组成,即G=(V, E),和树类似,其顶点又称为结点,并且边是有意义的,如上图(0,1)称为一条边。

上图中黑色的带数字的点就是顶点,可抽象成某个事物或对象。顶点之间的线就是边,表示事物与事物之间的关系。

需要注意的是在图论中边表示的是顶点之间的逻辑关系,粗细长短都无所谓的。包括上面的顶点也一样,表示逻辑事物或对象,画的时候大小形状都无所谓。

顶点(vertex)的属性:

度数(degree),与该顶点相关联的总边数,一个图G的总度数d(V)等于总边数2倍:2E。当图的边具有方向时,一个顶点又分为出度(out-degree)和入度(in-degree),出度是以该顶点为起点的边数,入度是以该顶点为终点的边数。

阶数(order),图G中顶点集V的大小为G的阶数。

边又称为线(line)或弧(arc),边(u,v)中表示u和v邻接(adjacent),(u, v) ∈ E,边(edge)的属性:

一条边是一个顶点对(u,v),u, v ∈ V,按照图的顶点对是否有序,顶点对有序的图称为有向图(directed graph, digraph,此时边(u,v)和(v,u)是两条不同的边,顶点对无序的图称为无向图(undirected graph),此时边(u,v)和(v,u)是两条相同的边,无向图可看作一个特殊的有向图。

有向边: 有向边就是固定这一条边只能从x通往y,y通往x则不可以。

无向边 : 无向边就是一条边可以从x通往y,y也可以通往x。

自环边: 一条边的起点终点是一个点。

平行边: 两个顶点之间存在多条边相连接。

有权图: 权值就是一条边的长度或代价。

无权图: 不是边的权值为0,而是全都为1。

稀疏图: 每个顶点的度数较小的图,如下图:

稠密图: 每个顶点的度数较大的图,如下图:

稀疏图:有很少条边或弧(边的条数|E|远小于|V|²)的图称为稀疏图(sparse graph)。

稠密图:有很多条边或弧 (边的条数|E|接近|V|²) 的图称为稠密图(dense graph)。 简单来说:我们假设某个图的点的个数 为 N, 边的个数为 M, 当 M << N ^ 2 (平方)(当边数远小于点的平方)时称为 稀疏图,当 M ≈ N ^ 2 (当边数约等于点的平方)时称为 稠密图, 如果图为稀疏图的时候,我们一般用邻接表储存,稠密图的时候,一般用邻接矩阵存储。

三、图的储存

那么,该怎么去储存图呢?

1.邻接矩阵

邻接矩阵是二维数据 :例如,g[ ][ ] 时空间需求为V^2,这种存储方式适合存储稠密图

邻接矩阵每一位存的是一条边 行代表的是起始点 ,列代表的是终止点,而里面存的值就是这条边的权值

一个点到自己的距离是0,到没有直接边连接的点的权值是无穷大

需要注意的是,有向图与无向图的矩阵不同,对于无向图,当vi、vj之间由边,则a[i][j]=a[j][i]=1,但是有向图,若i指向j,只有a[i][j]=1,a[j][i]=0

邻接矩阵要初始化

如下:

for(int i = 1; i <= n1; i++)// n1 为数组第一维大小
    {
        for(int j =  1; j <= n2; j++)// n2 为数组第一维大小
        {
           g[i][j] = g[j][i] =  0 ;
        }
    }

也可以借助memset来快速地将一个数组中的所有元素都初始化为0。

上面的代码等价于:

memset(g, 0, sizeof(g));

注意,memset只能用来初始化0和 -1,并且需要加上头文件cstring。

cin>>n>>m;//n表示点的数量,m表示边的数量
for(int i = 1;i <= m; i++){//枚举输入边
    cin>>x>>y>>z;//如上描述
    dis[x][y] = z;    //有向边:
    dis[x][y] = dis[y][x] = z;    //无向边:
}

2.邻接表

又叫链式前向星,其实就是链表。邻接表的思想是,对于图中的每一个顶点,用一个数组来记录这个点和哪些点相连。

如果用邻接矩阵表示稀疏图就会浪费大量内存空间,而用链接表,则是通过把顶点所能到的顶点的边保存在链表中来表示图。

步骤

1.用 h 数组保存各个节点能到的第一个节点的编号。开始时,h[i] 全部为 -1。

2.用 e 数组保存节点编号,ne 数组保存 e 数组对应位置的下一个节点所在的索引。

3.用 idx 保存下一个 e 数组中,可以放入节点位置的索引

4.插入边使用的头插法,例如插入:a->b。首先把b节点存入e数组,e[idx] = b。然后 b 节点的后继是h[a],ne[idx] = h[a]。最后,a 的后继更新为 b 节点的编号,h[a] = idx,索引指向下一个可以存储节点的位置,idx ++ 。

模板如下:

//邻接表
const int N = 100010, M = N * 2;
//无向图n条边时,最多2n个idx,因为每条边在邻接表中会出现两次
int h[N],  w[N], e[M], ne[M], idx;
//n个链表头,e每一个结点的值,ne每一个结点的next指针
// 添加一条边a->b
void add(int a, int b, int c)
{//e记录当前点的值(地址->值),ne下一点的地址(地址->地址),h记录指向的第一个点的地址(值->地址)
    e[idx] = b, w[idx] = c, ne[idx] = h[a], h[a] = idx ++ ;
}
// 初始化
idx = 0;
memset(h, -1, sizeof h);

3.邻接矩阵与邻接表的优缺点对比

空间方面:当图的顶点数很多、但是边的数量很少时,如果用邻接矩阵,我们就需要开一个很大的二维数组,最后我们需要存储 n*n 个数。但是用邻接表,最后我们存储的数据量只是边数的两倍。

效率方面:用邻接表存图的最大缺点就是随机访问效率低。比如,我们需要询问点 a 是否和点 b 连,我们就要遍历G[a],检查这个vector里是否有 b 。而在邻接矩阵中,只需要根据G[a][b]就能判断。

邻接表可以记录重复边:如果两个点之间有多条边,用邻接矩阵只能记录一条,但是用邻接表就能记录多条。虽然重复的边看起来是多余的,但在很多时候对解题来说是必要的。

因此,我们需要对不同的应用情景选择不同的存图方法。

如果是稀疏图(顶点很多、边很少),一般用邻接表;

如果是稠密图(顶点很少、边很多),一般用邻接矩阵。

到此这篇关于C++详细讲解图论的基础与图的储存的文章就介绍到这了,更多相关C++图论内容请搜索我们以前的文章或继续浏览下面的相关文章希望大家以后多多支持我们!

(0)

相关推荐

  • C++详细讲解图的拓扑排序

    目录 一.前言 二.算法流程 三.有向图的拓扑排序 一.前言 且该序列必须满足下面两个条件: 每个顶点出现且只出现一次. 若存在一条从顶点 x到顶点 y的路径,那么在序列中顶点 x 出现在顶点 y的前面. 拓扑排序只适用于 AOV网 (有向无环图) 若图中有环,则一定不存在拓扑序. 可以证明,一个有向无环图,一定存在一个拓扑序列.有向无环图,又被称为拓扑图. 入度: 即有多少条边指向自己这个节点. 出度: 即有多少条边从自己这个节点指出去. 二.算法流程 算法流程: 用队列来执行 ,初始化所有入

  • C++详细讲解图的遍历

    目录 图的遍历 图的深度优先遍历(DFS, depth first search) 图的宽度优先遍历(BFS, breadth first search) 宽度优先搜索BFS的应用 深度优先遍历DFS的应用 图的遍历 要想遍历图,肯定要先储存图啊. 下面我们采用邻接表来存图 也可以看: 点这里 1.用 h 数组保存各个节点能到的第一个节点的编号.开始时,h[i] 全部为 -1. 2.用 e 数组保存节点编号,ne 数组保存 e 数组对应位置的下一个节点所在的索引. 3.用 idx 保存下一个 e

  • 详解C++实现拓扑排序算法

    一.拓扑排序的介绍 拓扑排序对应施工的流程图具有特别重要的作用,它可以决定哪些子工程必须要先执行,哪些子工程要在某些工程执行后才可以执行.为了形象地反映出整个工程中各个子工程(活动)之间的先后关系,可用一个有向图来表示,图中的顶点代表活动(子工程),图中的有向边代表活动的先后关系,即有向边的起点的活动是终点活动的前序活动,只有当起点活动完成之后,其终点活动才能进行.通常,我们把这种顶点表示活动.边表示活动间先后关系的有向图称做顶点活动网(Activity On Vertex network),简

  • C++实现拓扑排序(AOV网络)

    本文实例为大家分享了C++实现拓扑排序的具体代码,供大家参考,具体内容如下 一.思路 先扫描所有顶点,把入度为0的顶点(如C,E)进栈.然后,取栈顶元素,退栈,输出取得的栈顶元素v(即入度为0的顶点v).接着,把顶点v的邻接顶点w的入度减1,如果w的入度变为0,则进栈.接着,取顶点w的兄弟结点(即取顶点v的邻接顶点w的下一邻接顶点),做同样的操作.重复上面步骤,直到输出n个顶点. 如上图: (1)扫描所有顶点,把入度为0的顶点进栈:将顶点C,E进栈: (2)取栈顶元素,退栈,输出取得的栈顶元素E

  • C++详细讲解图论的基础与图的储存

    目录 一.前言 二.图的定义 三.图的储存 1.邻接矩阵 2.邻接表 3.邻接矩阵与邻接表的优缺点对比 一.前言 在算法中一般都需要把问题抽象成图论问题然后用图论的算法解决问题. 图论涉及相当多的算法,包括图DFS和BFS.连通性.拓扑排序.最小生成树.最短路径.最大流网络.图的着色问题等等 图论算法在计算机科学中扮演着很重要的角色,它提供了对很多问题都有效的一种简单而系统的建模方式.很多问题都可以转化为图论问题,然后用图论的基本算法加以解决. 二.图的定义 图(graph) 如上图是一个图G,

  • Vue echarts画甘特图流程详细讲解

    vue项目中添加echarts,只需要增加echarts依赖,然后在main.js中引入echarts就可以使用了. 1.npm install echarts --save 2.修改main.js import * as echarts from 'echarts' Vue.prototype.$echarts = echarts 3.具体页面使用: <template> <div class="about"> <h1>This is echart

  • Python 数据可视化超详细讲解折线图的实现

    绘制简单的折线图 在使用matplotlib绘制简单的折线图之前首先需要安装matplotlib,直接在pycharm终端pip install matplotlib即可 使用matplotlib绘制简单的折线图,再对其进行定制,实现数据的可视化操作 import matplotlib.pyplot as plt # 导入pyplot模块并设置别名为plt squares = [1, 4, 9, 16, 25] plt.plot(squares) plt.show() # 打开matplotib

  • Redis超详细讲解高可用主从复制基础与哨兵模式方案

    目录 高可用基础---主从复制 主从复制的原理 主从复制配置 示例 1.创建Redis实例 2.连接数据库并设置主从复制 高可用方案---哨兵模式sentinel 哨兵模式简介 哨兵工作原理 哨兵故障修复原理 sentinel.conf配置讲解 哨兵模式的优点 哨兵模式的缺点 高可用基础---主从复制 Redis的复制功能是支持将多个数据库之间进行数据同步,主数据库可以进行读写操作.当主数据库数据发生改变时会自动同步到从数据库,从数据库一般是只读的,会接收注数据库同步过来的数据. 一个主数据库可

  • C语言超详细讲解循环与分支语句基础

    目录 写在开始 1. 分支语句 1.1 if语句 1.2 switch 2. 循环语句 2.1 while()语句 2.2 do while()语句 2.3 for 语句 for语句中表达式的省略 break在循环语句中的作用 continue 在循环语句中的应用 总结: 写在开始 在内容开始之前给大家介绍一下在计算机中如何表示真假 0表示假,非0表示真. 1. 分支语句 分支语句也叫做条件选择语句,主要分为if语句和switch语句. 1.1 if语句 if()…{} else if()…{}

  • C++类模板与函数模板基础详细讲解

    目录 函数模板 类模板 总结 函数模板 当我们想要定义一个可以支持泛型的函数时,就要采用函数模板的方式了.所谓泛型就是可以支持多种类型的操作,比如我们定义一个compare操作,他可以根据传递给他的参数类型动态调用对应的函数版本,实现多种类型的比较. template <typename T> int compare(const T &v1, const T &v2) { if (v1 < v2) return -1; if (v2 < v1) return 1;

  • Android同步异步任务与多线程及Handler消息处理机制基础详细讲解

    目录 一.同步与异步 Android中的多线程 Android中的多线程与主线程与子线程 Handler异步通信系统 使用新线程计算质数 一.同步与异步 同步的执行任务:在执行程序时,如果没有收到执行结果,就一直等,不继续往下执行,直到收到执行结果,才接着往下执行. 异步的执行任务:在执行程序时,如果遇到需要等待的任务,就另外开辟一个子线程去执行它,自己继续往下执行其他程序.子线程有结果时,会将结果发送给主线程 Android中的多线程 线程:通俗点讲就是一个执行过程.多线程自然就是多个执行过程

  • JavaWeb ServletContext基础与应用详细讲解

    目录 ServletContext 基础知识 获取 ServletContext对象 特性 context-param 获取文件路径 记录日志 参数增删改查 ServletContext 基础知识 获取 ServletContext对象 有两种方式可以获取: 使用 servletconfig 对象获取 使用 servlet 上下文获取 // 第一种方式 ServletContext app1 = config.getServletContext(); writer.println("<br

  • socket编程的详细讲解

    目录 1:socket大致介绍 2:TCP/IP协议 3:回过头再来理解socket 4:socket的一些接口函数原理 5:socket的一个例子,总结上述的问题 6:上面例子用到的知识点 7:下面就介绍一些API函数: socket编程是网络常用的编程,我们通过在网络中创建socket关键字来实现网络间的通信,通过收集大量的资料,通过这一章节,充分的了解socket编程,文章用引用了大量大神的分析,加上自己的理解,做个总结性的文章 1:socket大致介绍 socket编程是一门技术,它主要

  • 超详细讲解Java线程池

    目录 池化技术 池化思想介绍 池化技术的应用 如何设计一个线程池 Java线程池解析 ThreadPoolExecutor使用介绍 内置线程池使用 ThreadPoolExecutor解析 整体设计 线程池生命周期 任务管理解析 woker对象 Java线程池实践建议 不建议使用Exectuors 线程池大小设置 线程池监控 带着问题阅读 1.什么是池化,池化能带来什么好处 2.如何设计一个资源池 3.Java的线程池如何使用,Java提供了哪些内置线程池 4.线程池使用有哪些注意事项 池化技术

随机推荐