6.4 图的存储结构

 思维导图:

 

 前言:

---

**6.4 图的存储结构**

- **核心问题**:由于图的结构复杂性,我们不能仅仅依赖于元素在存储区的物理位置来表示它们之间的关系。

- **邻接矩阵**:
  - **基本思路**:虽然图没有顺序存储结构,但我们可以用二维数组来模拟元素之间的关系。
  - **适用情境**:当你需要快速判断两个顶点之间是否存在边时。

- **链式存储**:
  - **基本思路**:由于图中任意两点可能都有关系,使用链式存储是很自然的选择。
  - **种类**:
    - 邻接表:适合于表示稀疏图。
    - 十字链表:适合于有向图。
    - 邻接多重表:适合于既有边又有弧的图。
  - **选择依据**:根据实际应用和需要来选择最适合的链式存储结构。

---

6.4.1 邻接矩阵

**1. 概述**
邻接矩阵是用于表示图中顶点之间关系的矩阵。对于一个含有n个顶点的图,其邻接矩阵是一个n阶方阵。该矩阵能够表示顶点之间是否存在边,以及边的权值。

**2. 创建无向网的邻接矩阵**

**算法6.1:采用邻接矩阵表示法创建无向网**

- **步骤:**
    1. 输入总顶点数和总边数。
    2. 依次输入点的信息并保存到顶点表中。
    3. 初始化邻接矩阵,将每个权值设置为极大值。
    4. 构建邻接矩阵。输入每条边的顶点和其权值,然后在图中确定两个顶点的位置,赋予边相应的权值,并使其对称边具有相同的权值。

- **算法描述:**
    1. 输入总顶点数和总边数。
    2. 输入每个顶点的信息。
    3. 初始化邻接矩阵的权值为极大值。
    4. 构建邻接矩阵:输入边的顶点和权值,然后确定顶点在图中的位置,并设置边的权值。

**3. 邻接矩阵的优缺点**

- **优点:**
    1. 可以轻松判断两个顶点之间是否存在边。
    2. 方便计算每个顶点的度。

- **缺点:**
    1. 增加和删除顶点不方便。
    2. 统计边的数量不便,因为需要检查所有邻接矩阵的元素,时间复杂度为O(n²)。
    3. 空间复杂度高。对于有n个顶点的有向图,需要n²个单元来存储边。对于无向图,由于邻接矩阵是对称的,可以使用压缩存储的方式。

6.4.2 邻接表 

### 定义:
邻接表(Adjacency List)是图的一种链式存储结构。

### 结构:
1. **表头节点表**:
   - 表示方式: 由所有表头节点以顺序结构的形式存储,便于随机访问任一顶点的边链表。
   - 包含:数据域(data)和链域(firstarc)。
     - 数据域: 用于存储顶点v的名称或其他相关信息。
     - 链域: 指向链表中第一个节点(与顶点v邻接的第一个邻接点)。
   - 示意图:图6.11(a)展示了表头节点的结构。

2. **边表**:
   - 表示方式: 由表示图中顶点间关系的2n个边链表组成。
   - 包含:邻接点域(adjvex)、数据域(info)和链域(nextarc)。
     - 邻接点域: 指示与顶点v邻接的点在图中的位置。
     - 数据域: 存储和边相关的信息,如权值等。
     - 链域: 指示与顶点v邻接的下一条边的节点。
   - 示意图:图6.11(b)展示了边节点的结构。

### 特点:
- 对于每个顶点v,建立一个单链表,其中列出与v相邻接的所有顶点。
- 在无向图的邻接表中,顶点v的度是第i个链表中的节点个数。
- 在有向图中,第i个链表中的节点个数是顶点v的出度。为计算入度,必须遍历整个邻接表。邻接点域的值为i的节点个数表示顶点v的入度。
- 有时,为了确定顶点的入度,可以创建一个有向图的逆邻接表。逆邻接表是针对每个顶点v,建立一个链表,该链表连接所有进入v的边。

### 逆邻接表:
- 定义:对于每个顶点v,逆邻接表建立一个链表,该链表连接所有进入v的边。
- 用途:方便地确定顶点的入度,而不需要遍历整个邻接表。
- 示意图:图6.12(c)展示了有向图G的逆邻接表。

### 示例:
- 如图6.12(a)所示,这是图6.1中G₁的邻接表。
- 如图6.12(b)所示,这是图6.1中G₂的邻接表。
- 如图6.12(c)所示,这是有向图G的逆邻接表。

### 小结:
邻接表是图的一种有效的链式存储结构,尤其适合于稀疏图。表头节点表存储顶点的信息,而边表存储与特定顶点相邻接的所有顶点。对于有向图,除了邻接表外,还可以构建逆邻接表来便于确定顶点的入度。

笔记:图的邻接表存储结构及优缺点

### 图的邻接表存储结构:

1. **定义**:邻接表是图的一种链式存储结构。其中包括顶点的头节点和表示边的边节点。
   
2. **组成**:
   - **顶点信息 (VNode)**:
     - data: 存放顶点数据。
     - firstarc: 指向第一条依附该顶点的边的指针。
   - **边节点 (ArcNode)**:
     - adjvex: 该边所指向的顶点的位置。
     - nextarc: 指向下一条边的指针。
     - info: 存放和边相关的信息。

3. **构建无向图的过程**:
   - 输入总顶点数和总边数。
   - 输入点的信息并初始化每个表头节点的指针域为NULL。
   - 创建邻接表。对每条边,确定这两个顶点的序号后,将此边节点分别插入两个对应的边链表的头部。

4. **算法分析**:该算法的时间复杂度是O(n+e),其中n为顶点数,e为边数。

### 邻接表的优缺点:

**优点**:
1. 便于增加和删除顶点。
2. 便于统计边的数目。按顶点表顺序查找所有边表可以得到边的数目,时间复杂度为O(n+e)。
3. 空间效率高。适合表示稀

疏图。特别是对于一个具有n个顶点、e条边的图G, 如果G是无向图,邻接表表示中有n个顶点表节点和2e个边表节点;如果G是有向图,邻接表或逆邻接表表示中均有n个顶点表节点和e个边表节点。这意味着其空间复杂度为O(n+e)。而对于稠密图,由于邻接表中需要额外的链域,所以通常使用邻接矩阵表示法。

**缺点**:
1. 判断顶点之间是否有边不太方便。要判断两顶点v和v'之间是否存在边,可能需要查找整个边表。在最坏的情况下,时间复杂度可能达到O(n)。
2. 在有向图中计算顶点的度(进度和出度)不便捷。对于无向图,在邻接表中顶点v的度是其边表中的节点数量。但在有向图的邻接表中,边表中的节点数表示顶点v的出度,而计算其入度则较为困难,需要遍历所有顶点的边表。如果有向图使用逆邻接表表示,那么与邻接表表示正好相反:计算顶点的入度会很简单,但计算其出度会比较困难。

### 补充:
- 值得注意的是,图的邻接矩阵表示是唯一的,但其邻接表表示不是唯一。这是因为在邻接表表示中,各边表节点的链接次序取决于建立邻接表的算法和边的输入顺序。
- 邻接矩阵和邻接表是图的两种常用的存储结构,各自有其优点和缺点。选择哪种表示方法取决于具体的应用场景和所需的操作。
  
接下来的内容介绍十字链表,这是有向图的另一种存储结构,特别设计来方便地获取顶点的入度和出度。

**十字链表**:
十字链表是有向图的一种链表存储结构。在这种表示中,每个顶点关联两个链表:一个用于入度,另一个用于出度。

每个顶点都有一个表头。表头的两个字段指向两个链表:一个指向第一个入度边,另一个指向第一个出度边。每个边节点有几个字段,包括指向其起点和终点的指针、指向起点和终点的下一个边的指针以及存储与边相关的其他信息的字段。

**优点**:
1. 易于获取顶点的入度和出度。
2. 与邻接表相比,空间复杂度仍为O(n+e),但提供了更多的方便性。

**缺点**:
1. 相对于邻接表,十字链表的结构更加复杂。
2. 对于无向图,使用十字链表可能过于冗余。

总的来说,选择最适合的图存储结构取决于所处理的具体问题和操作的频率。邻接矩阵在某些情境下可能更适合,而在其他情境下,邻接表或十字链表可能是更好的选择。

6.4.3 十字链表

### 6.4.3 十字链表

- **定义**:十字链表(Orthogonal List)是有向图的另一种链式存储结构。它将有向图的邻接表和逆邻接表结合起来,得到的一种链表。

- **组成**:  
  * **弧节点(ArcBox)**:  
    - tailvex:指示弧尾的顶点位置。
    - headvex:指示弧头的顶点位置。
    - hlink:指向弧头相同的下一条弧。
    - tlink:指向弧尾相同的下一条弧。
    - info:指向该弧的相关信息。

  * **顶点节点(VexNode)**:
    - data:存储与顶点相关的信息,例如顶点的名称。
    - firstin:指向以该顶点为弧头的第一个弧节点。
    - firstout:指向以该顶点为弧尾的第一个弧节点。

- **图示**:图6.14展示了一个有向图与其对应的十字链表。
  - (a)有向图。
  - (b)十字链表。

- **性质**:
  1. 若将有向图的邻接矩阵看作稀疏矩阵,那么十字链表可以看作邻接矩阵的链式存储结构。
  2. 弧节点所在的链表是非循环的,节点之间的相对位置自然形成,不必按照顶点序号排列。
  3. 顶点节点之间的关系是顺序存储,而不是链接关系。- **存储表示**:

#define MAX_VERTEX_NUM 20typedef struct ArcBox {int tailvex, headvex;        // 弧的尾和头顶点的位置struct ArcBox *hlink, *tlink; // 分别为弧头相同和弧尾相同的弧的链域InfoType *info;             // 该弧的相关信息
} ArcBox;typedef struct VexNode {VertexType data;            // 与顶点相关的信息ArcBox *firstin, *firstout; // 分别指向该顶点的第一条入弧和出弧
} VexNode;typedef struct {VexNode xlist[MAX_VERTEX_NUM];int vexnum, arcnum;        // 有向图的当前顶点数和弧数
} OLGraph;

- **时间复杂度**:建立十字链表的时间复杂度与建立邻接表相同。使用十字链表可以容易地找到以v为尾的弧和以v为头的弧,从而容易求得顶点的出度和入度。

- **应用**:十字链表在某些有向图的应用中是一个非常有用的工具。

> **备注**:给定的信息片段是断断续续的,可能部分信息缺失或不完整。上面的笔记是根据提供的内容尽量整理得出的。

6.4.4 邻接多重表

**定义**: 
邻接多重表(Adjacency Multilist)是无向图的链式存储结构,与邻接表类似,但在邻接多重表中,每条边用一个节点来表示,而不是两个。

**优势**: 
相比邻接表,邻接多重表在某些对边操作的应用中更为方便。例如,对已经搜索过的边进行标记或删除某条边等,都更容易在邻接多重表中进行。

**结构描述**:

1. **边节点** (图6.15a):
   - **mark**: 标志域,用以标记该条边是否被搜索过。
   - **ivex, jvex**: 该边依附的两个顶点在图中的位置。
   - **ilink**: 指向下一条依附于顶点ivex的边。
   - **jlink**: 指向下一条依附于顶点jvex的边。
   - **info**: 指针域,指向和边相关的信息。

2. **顶点节点** (图6.15b):
   - **data**: 存储与顶点相关的信息。
   - **firstedge**: 指示第一条依附于该顶点的边。

**示例**:
图6.16展示了无向图G₂的邻接多重表结构。可以看出,与邻接表相比,邻接多重表中每条边只用一个节点表示,减少了存储冗余。**邻接多重表的数据结构表示**:

#define MAX_VERTEX_NUM 20
typedef enum {unvisited, visited} VisitIf;typedef struct EBox {VisitIf mark;int ivex, jvex;struct EBox *ilink, *jlink;InfoType *info;
} EBox;typedef struct VexBox {VertexType data;EBox *firstedge;
} VexBox;typedef struct {VexBox adjmulist[MAX_VERTEX_NUM];int vexnum, edgenum;
} AMLGraph;

总结:
邻接多重表主要应用于需要频繁对边进行操作的场合。其优点是每条边只用一个节点表示,减少了存储冗余和某些操作的复杂度。但其也继承了链表的一些特性,如需要额外的指针域存储。选择适当的数据结构,取决于具体应用和操作的需求。

总结:

### 图的存储结构

图是一种非线性的数据结构,常用的存储结构有:邻接矩阵、邻接表、邻接多重表、十字链表等。

#### 1. 邻接矩阵

**重点**:
- 使用一个二维数组表示顶点间的关系。
- 对于无向图,邻接矩阵是对称的;对于有向图,不一定是对称的。

**难点**:
- 空间利用不高。对于稀疏图,邻接矩阵的大多数元素都是0,导致浪费。

**易错点**:
- 混淆有向图和无向图的矩阵表示。

#### 2. 邻接表

**重点**:
- 适合存储稀疏图。
- 每个顶点对应一个链表,链表中的元素表示与该顶点相邻的顶点。

**难点**:
- 链表的操作需要熟练,如插入、删除节点等。
- 对于无向图,每条边会在两个链表中各出现一次。

**易错点**:
- 未正确处理链表操作可能导致内存泄漏或错误。

#### 3. 邻接多重表

**重点**:
- 无向图的存储结构。
- 每条边只使用一个节点表示。

**难点**:
- 节点结构相对复杂,需要处理多个指针域。

**易错点**:
- 容易混淆邻接表和邻接多重表的表示。
- 指针操作容易出错。

#### 4. 十字链表

**重点**:
- 用于有向图。
- 每个顶点有两个指针,一个指向出边链表,一个指向入边链表。

**难点**:
- 相比邻接表,结构更为复杂。

**易错点**:
- 指针操作需要特别小心,容易导致链表断裂或循环引用。

#### 总结

**重点**:
- 了解每种存储结构的特点和适用场景。
- 理解顶点和边在不同结构中的表示方法。

**难点**:
- 不同结构间的转换。
- 根据特定应用选择合适的存储结构。

**易错点**:
- 指针操作、内存管理、特别是在链表相关的存储结构中。
- 混淆不同存储结构的特点和表示方式。

选择合适的存储结构对于图算法的效率至关重要。理解每种结构的特点和适用场景,能够在实际应用中做出合适的选择。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/109385.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

[Machine Learning][Part 6]Cost Function代价函数和梯度正则化

目录 拟合 欠拟合 过拟合 正确的拟合 解决过拟合的方法:正则化 线性回归模型和逻辑回归模型都存在欠拟合和过拟合的情况。 拟合 来自百度的解释: 数据拟合又称曲线拟合,俗称拉曲线,是一种把现有数据透过数学方法来代入一条…

PHP框架开发实践 | 1024 程序员节:通过index.php找到对应的controller是如何实现的

🏆作者简介,黑夜开发者,CSDN领军人物,全栈领域优质创作者✌,CSDN博客专家,阿里云社区专家博主,2023年6月CSDN上海赛道top4。 🏆数年电商行业从业经验,历任核心研发工程师…

STM32CubeMX之DMA辅助串口数据接收

1.DMA辅助串口数据接收 1.1 DMA简介 直接存储器存取(DMA)用来提供在外设和存储器之间或者存储器和存储器之间的高速数据传输。无须CPU干预,数据可以通过DMA快速地移动,这就节省了CPU的资源来做其他操作。   两个DMA控制器有12个通道(DMA1有7个通道&am…

什么是网络爬虫,爬虫的机制是那些

网络爬虫(也称为网页蜘蛛、网络机器人或网页追逐者)是一种按照预设规则,自动抓取万维网信息的程序或脚本。它们广泛应用于搜索引擎、数据挖掘、竞争情报、价格监测等各种互联网应用中。 爬虫机制是爬虫程序或机器人用来访问、抓取、索引以及…

2023年中国清净剂行业需求现状及前景分析[图]

清净剂用于中和由于燃烧和润滑油氧化产生的酸性物质,并清除颗粒和污物。这类杂质在油中的溶解度有限,因此,清净剂可以最大程度减少沉积物的生成,降低污染,提高环保排放标准。成熟产品有磺酸盐、硫化烷基酚盐、烷基水杨…

yolov作者简介

作者简介 作者叫Joseph Redmon,在谷歌学术上搜索作者的简介。 地址:‪Joseph Redmon‬ -巨人学术搜索‬‬ (cljtscd.com) 他提出了最著名的YOLO算法。其中YOLOV1的引用量达到了40287次。 gitihub地址:github地址 主页:个人主页

ims-ui项目搭建

node版本: npm版本: 创建vite项目: npm create vitelatest 使用的vite版本为: 安装router4,安装命令如下: npm install vue-router4 安装pinia,安装命令如下: npm install pinia 安装Pinia持…

【Linux】adduser命令使用

我们经常在linux系统中创建用户。有时候用的是 useradd 有时候用的是 adduser ,好混乱啊到底用哪个啊。今天咱们一起来学习一下。 adduser与useradd的区别 useradd 命令是内置的 Linux 命令,在任何 Linux 系统中都可用。然而,使用这种低级…

ssm+vue基本微信小程序的今日菜谱系统

项目介绍 谈到外出就餐,我们除了怕排队,也怕这家餐厅的服务员不够用,没人为我们点餐,那么一餐饭排队一小时,点餐恐怕也要花个半小时,这样不仅给消费者的用餐体验大打折扣同时也给商家的口碑造成了严重负面…

代码覆盖率统计Super-jacoco在公司级容器化项目中的具体应用方案

目录 一、介绍 二、自己在本地搭建Super-jacoco服务 2.1 准备工作 2.2 部署super jacoco服务 1、下载super jacoco 项目 2、初始化数据库 3、配置application.properties 4、编译super jacoco项目 5、部署 super jacoco 服务 2.3 启动被测项目 2.4、代码覆盖率收集 2…

mac虚拟机安装homebrew时的问题

安装了mac虚拟机&#xff0c;结果在需要通过“brew install svn”安装svn时&#xff0c;才注意到没有下载安装homebrew。 于是便想着先安装homebrew&#xff0c;网上查的教程大多是通过类似以下命令 “ruby <(curl -fsSkL raw.github.com/mxcl/homebrew/go)” 但是都会出现…

RFID智能制造应用:助力企业提升制造效率!

随着企业间竞争加剧&#xff0c;如何提升企业生产效率&#xff0c;降低成本成为不少制造企业持续追求的目标。利用智能制造中的RFID设备&#xff0c;可以对企业入库、盘点、生产、出库等流程进行监控&#xff0c;本文将探讨智能制造中的RFID设备如何帮助企业提升制造效率&#…

【C/C++笔试练习】内联函数、缺省参数、函数重载、类定义、不要二、字符串转成整数、Fibonacci数列、合法括号序列判断

文章目录 C/C笔试练习1.内联函数&#xff08;1&#xff09;内联函数的使用&#xff08;2&#xff09;内联函数的使用 2.缺省参数&#xff08;3&#xff09;缺省参数概念理解 3.函数重载&#xff08;4&#xff09;函数重载的定义&#xff08;5&#xff09;函数重载的定义 4.类定…

python+selenium自动化测试环境搭建步骤(selenium环境搭建)

一、自动化简介 1.自动化测试概念&#xff1a; 是把以人为驱动的测试转化为机器执行的一种过程&#xff0c;它是一种以程序测试程序的过程 2.自动化测试分类&#xff1a; 一般IT上所说的自动化测试是指功能自动化测试&#xff0c;通过编码的方式用一段程序来测试一个软件的…

【C++11】智能指针的使用以及模拟实现(shared_ptr,unique_ptr,auto_ptr,weak_ptr)

&#x1f30f;博客主页&#xff1a; 主页 &#x1f516;系列专栏&#xff1a; C ❤️感谢大家点赞&#x1f44d;收藏⭐评论✍️ &#x1f60d;期待与大家一起进步&#xff01; 文章目录 一、 RAII概念一、auto_ptr1.基本使用2.模拟实现 二、unique_ptr1.基本使用2.模拟实现…

网络层哪些事?

在本文讲解的网络层中&#xff0c;注意了解一下&#xff1a;IP协议&#xff01; 地址管理&#xff1a;每个网络上的设备&#xff0c;要能分配一个地址&#xff08;唯一&#xff09;路由选择&#xff1a;A给B发消息&#xff0c;具体走哪条路线&#xff1f;&#xff1f; IP地址&…

Linux/Ubuntu 安装 Java运行环境

linux下安装Java运行环境 1、下载安装包 .tar.gz 先在官网下载 JDK 点击这里 在这里要选择对应的 JDK 版本&#xff0c;一般我们目前选择JDK8 点击这里 2、在 /usr/local/ 目录下创建Java文件夹 cd /usr/local/ mkdir java3、将下载的文件通过FTP程序上传到刚刚创建的Java文…

服务器往浏览器推消息(SSE)应用

1&#xff0c;SSE 和 WebSocket 对比 SSE&#xff08;服务器发送事件&#xff09; SSE是一种基于HTTP的单向通信机制&#xff0c;用于服务器向客户端推送数据。它的工作原理如下&#xff1a; 建立连接&#xff1a;客户端通过发送HTTP请求与服务器建立连接。在请求中&#xff…

git学习——第4节 时光机穿梭

我们已经成功地添加并提交了一个readme.txt文件&#xff0c;现在&#xff0c;是时候继续工作了&#xff0c;于是&#xff0c;我们继续修改readme.txt文件&#xff0c;改成如下内容&#xff1a; Git is a distributed version control system. Git is free software. 现在&…

游戏中的随机——“动态平衡概率”算法

前言 众所周知计算机模拟的随机是伪随机&#xff0c;但在结果看来依然和现实中的随机差别不大。 例如掷硬币&#xff0c;连续掷很多很多次之后&#xff0c;总有连续七八十来次同一个面朝上的情况出现&#xff0c;计算机中一般的随机函数也能很好模拟这一点。 但在游戏中&…