数据结构系列15之图的存储方式2

一.十字链表和邻接多重表

1.十字链表(专门优化有向图的邻接表)

核心问题:

普通邻接表存储有向图时,入边和出边分离------想查一个顶点的所有出度,直接查其邻接链表;想查所有入度,却需要遍历整个图的所有邻接链表,效率极低

​ 设计逻辑:

将有向图的每一条边设计为一个独立的节点,节点中同时记录: 起点、终点、起点的下一条出边、终点的下一条入边;同时为每个顶点维护两个指针: 出度表头 (指向该顶点的第一条出度)、 入度表头 (指向该顶点的第一条入度)

​核心优势:

出入度更方便,同时支持O(d)的出度和入度遍历,增删边时仅需修改相邻边节点的指针,无需遍历整个图,适配"频繁增删边、需要同时处理入度和出度"的有向图场景(如拓扑排序、关键路径的频繁操作)

十字链表相当于正邻接表逆邻接表的结合

但是空间利用率更高

2.邻接多重表(专门优化无向图的邻接表)

核心问题:

普通邻接表存储无向图时,同一条边被存储两次(i的链表存j,j的链表存i)------增删边时,需要同时找到并修改两个节点,操作繁琐且容易出错

​ 设计逻辑:

将无向图的每一条边设计为一个独立的节点,节点中记录: 边的两个顶点、顶点1的下一条邻接边、顶点2的下一条邻接边 ;每个顶点仅维护一个指针,指向其任意一条邻接边节点

​ 核心优势:

一条边仅存储一次,增删边时仅需操作一个边节点,修改其指针即可,适配"频繁删边、边数较多"的无向图场景

存储结构的核心选择原则(使用场景)

看"顶点数n"和"边数e"的比例,本质是"空间"和"效率"的权衡:

(1) 当e ≈ n²(稠密图):选邻接矩阵,空间浪费少,查询边快;

​(2) 当e ≈ n(稀疏图):选邻接表,空间利用率极高,遍历邻接顶点快;

​ (3)当需要频繁操作有向图的入边/出边:选十字链表;

​(4)当需要频繁增删无向图的边:选邻接多重表

二.代码

1.十字链表

1.1要点

删除:(和多重邻接表删除思想一样,重点考虑一条边两个顶点,在那两层分别找到这两个顶点)

遍历顶点 u 的 出边链,找到 <u, v> 弧,从出链摘除

​ 遍历顶点 v 的 入边链,找到 <u, v> 弧,从入链摘除

​ free(弧节点)

核心:

弧的结构定义:包含(弧尾)出度,(弧头)入度

顶点结构:序号,元素,入度/出度头节点

图头结构:定点结构,约束边,边总数

释放图时候,站在每一层顶点头出遍历删除释放,一层接着一层

在添加边时候对入度出度同时进行头插法的操作,变会构成图中十字链表结构

no编号的出度入度的计算: 站在该节点的那一层上,分别看tailNext / headNext的节点数即可

1.2代码

.h

.c

main.c

2.邻接多重表

2.1要点

删除操作:邻接多重表删除,找a,b节点,找到这个边,在释放

核心:

一条边,要找两个点;删除时,找到边,i不一定就是V1,j也不一定是V2

比如,从节点进来,V1在iLink上,就从iLink找,V1在jLink上就从jLink找; 提供找的方法 ;iLink链接的是以V1为起始(i)/终止(j)位置的下一个边

边的关系:以i为头节点往下走,以谁为节点到j

2.2代码

.h

.c

main.c

相关推荐
xiaoye-duck13 分钟前
《Linux系统编程》Linux 系统多线程(八): C++ 高并发线程池全链路深度解析与从零手撕实现
linux·c++·线程池
胖大和尚1 小时前
在C++的类中,是否可以把函数声明成__device__
c++·cuda
-dzk-1 小时前
【链表】LC 160.相交链表
数据结构·链表
十年磨剑走天涯1 小时前
C++ STL 容器操作复杂度速查表
开发语言·c++
ziguo11221 小时前
Windows API MessageBox 函数详解
c语言·c++·windows·visualstudio
王老师青少年编程1 小时前
2023年CSP-J初赛真题及答案解析(11-15)
c++·真题·csp-j·答案·csp·初赛·信奥赛
15Moonlight1 小时前
C++进阶(09):特殊类设计
开发语言·c++
AKA__Zas1 小时前
芝士算法(前缀和2.0)
java·数据结构·算法·leetcode·哈希算法·学习方法
caishenzhibiao2 小时前
降雨带波段点差 同花顺期货通指标
java·c语言·c#
皓月斯语2 小时前
B3867 [GESP202309 三级] 小杨的储蓄 题解
c++·算法·题解