408数据结构学习笔记——图的存储

简介: 408数据结构学习笔记——图的存储

1.邻接矩阵

8f598a28bd814484be7cf6589426cedb.png

1.1.邻接矩阵的定义

采用一维数组存放顶点数据,二维数组存放边的数据(各顶点是否邻接)

  1. 无向图:A[i][j] = 0,则图中Vi和Vj不邻接;A[i][j] = 1,则图中Vi和Vj邻接
  2. 有向图:A[i][j] = 0,则图中没有Vi指向Vj的边;A[i][j] = 1,则图中有Vi指向Vj的边
#define MAXVERTEXNUM 100    //顶点数最大值为100
typedef struct MGraph{
    char Vex[MAXVERTEXNUM];    //一维数组,存放顶点数据
    int Edge[MAXVERTEXNUM][MAXVERTEXNUM];    //二维数组,存放邻接矩阵
    int verNum, arcNum;    //顶点数,弧数
}MGraph;

1.2.求度、入度和出度

  1. 度:第 i 行或者第 i 列的非0元素的个数
  2. 入度:第 i 列非0元素的个数
  3. 出度:第 i 行非0元素的个数

1.3.邻接矩阵的性能

  • 空间复杂度O(gif.gif)(二维数组 顶点数*顶点数)
  • 适合存储稠密图
  • 如果是无向图,可以只存储上三角或者下三角(特殊矩阵的压缩)

1.4.邻接矩阵的性质

设图G的邻接矩阵为A,gif.gif的元素gif.gif[i][j]等于由顶点 i 到顶点 j 的长度为 n 的路径数目

2.邻接表

2.1.邻接表的定义

邻接表为图中每个顶点都建立一个单链表,该表中存放该顶点的所有邻接结点,并成为边表;而边表的头指针和顶点的数据信息采用顺序存储,称为顶点表

image.png

其中firstarc指向该顶点的第一个邻边,nextarc指向该顶点的下一个邻边

32fc31e0cf1245d4bf627bd4b7b578e0.png

#define MAXVERTEXNUM 100    //最大顶点数
typedef struct ArcNode{    //边表结点
    int adjevex;    //该弧指向的顶点的数组下标
    struct ArcNode *next;    //指向下一条弧的指针
    //InfoType info;    //网的边权值
}ArcNode;
typedef struct VNode{    //顶点表结点
    VertexType data;    //顶点信息
    ArcNode *first;    //指向第一条邻边的指针
}VNode, AdjList[MAXVERTEXNUM];
typedef struct{
    AdjList vertices;    //邻接表
    int vexNum, arcNum;    //顶点数和弧数
}ALGraph;

2.2.邻接表的空间复杂度

无向图中,每一条边都被保存了两次,因此空间复杂度为O(|V| + 2|E|)

有向图中,每一条边只被保存了一次,因此空间复杂度为O(|V| + |E|)

2.3.邻接表的度、入度和出度

度:遍历该顶点的边表,有几个则度就为几

出度:遍历该顶点的边表,有几个则出度就为几

入度:遍历除该顶点外的其他所有顶点边表,共有几个指向该顶点的弧,则入度就为几

2.4.邻接表与邻接矩阵的不同点

邻接矩阵:稠密图;确定顶点编号则图唯一

邻接表:稀疏图;图不唯一

3.十字链表(有向图)

十字链表弥补了邻接表查找入度困难的缺点,它既容易查找顶点的入度,也容易找顶点的出度

46075c6fa49140deb7bfb7735ada38be.png

顶点结点的data域存放数据,firstin域存放第一个指向它的弧的指针,firstout域存放第一个指出的指针

弧结点的tailvex域存放弧尾的顶点,haedvex域存放弧头的顶点,hlink域存放下一个弧头和该顶点相同的弧的指针,tlink域存放下一个弧尾和该顶点相同的弧的指针

找入度:从该顶点结点的firstin开始找,再进入弧结点的hlink

找出度:从该顶点结点的firstout开始找,再进入弧结点的tlink

382500c8f023444e9de81a38282987ce.png

4.邻接多重表(无向图)

邻接表存放无向图时,每条边会存放两次,浪费大量空间,因此,采用邻接多重表

邻接多重表的结构类似十字链表

09b988ae86c94045a2f03261a6497288.png

顶点结点:data存放顶点结点的数据;firstedge存放第一条依附于该节点的边

6aa7320d67a042539b047f35a285748d.png

5.图的基本操作

Adjacent(G, x, y);    //判断图是否存在边<x, y>或者(x, y)

最好时间复杂度O(1):该顶点的下一个边结点即为要搜索的边

最坏时间复杂度O(|V|):该顶点的最后一个边界点是要搜索的边O(|V| - 1)

Neighbors(G, x);    //列出图G与顶点x邻接的边

无向图:

  1. 最好时间复杂度O(1):该顶点只有一个边(邻接表)
  2. 最坏时间复杂度O(|V|):该顶点的边有V - 1条(邻接表);遍历整行n个顶点(邻接矩阵)

有向图:

  1. 最好时间复杂度O(1):出边:该顶点只有一个出边(邻接表)
  2. 最坏时间复杂度:遍历整行n个结点O(|V|)(邻接矩阵);入边:遍历全部边结点O(|E|)(邻接表)
InsertVertex(G, x);   //在图中插入顶点x 

时间复杂度为O(1):有向图、无向图、邻接矩阵、邻接表都一样(新顶点刚开始不连任何边)

DeleteVertex(G, x);    //在图中删除顶点x

无向图:

  1. 最好时间复杂度O(1):该顶点没有相邻的边(邻接表)
  2. 最坏时间复杂度:删除该顶点的行和列O(|V|)(邻接矩阵);该顶点邻接了尽可能多的边,则需要遍历其他所有边O(|E|)(邻接表)

有向图:

  1. 邻接矩阵:删除行列O(|V|)
  2. 邻接表:
  1. 删出边:O(1)没有出边,O(|V|)
  2. 删入边:O(|E|)遍历所有边
AddEdge(G, x, y);    //添加<x, y>或者(x, y)

邻接矩阵:O(1)顺序表随机存储特性

邻接表:O(1)头插法,O(|V|)尾插法

FirstNeighhbor(G, x);    //求图G中顶点x的第一个邻接顶点

无向图:

  1. 邻接矩阵:遍历行,最好时间复杂度第一个O(1),最坏时间复杂度最后一个或不存在O(|V|)
  2. 邻接表:顶点结点连着的第一个边结点O(1)

有向图:

  1. 邻接矩阵:出边行,入边列 O(1) ~ O(n)
  2. 邻接表:
  1. 入边O(1),指定节点的第一个边结点
  2. 出边O(1),遍历所有边O(1) ~ O(|E|)
NextNeighbor(G, x, y);    //顶点y是图G中顶点x的一个邻接点,返回除y外的下一个邻接点

6.王道课后题

84537713ed02425d94698d8464c40dbc.png

1.无向图:邻接矩阵中遍历上三角或者下三角,统计有多少个非零元素;邻接表中遍历所有边结点,边的数量为边结点的一半

有向图:邻接矩阵种遍历所有行,统计有多少个非零元素;邻接表中遍历所有边结点,边的数量即为边结点数量

2.无向图:邻接矩阵中查找第 i 行 j 列是否为非零;邻接表中遍历 i 的顶点结点相连的边结点是否有指向j的边结点

有向图:邻接矩阵中查找第 i 行 j 列和第 j 行 i 列是否为非零;邻接表中分别遍历 i 和 j 的顶点结点相连的边结点

3.无向图:邻接矩阵中遍历行,非零元素的个数即为度;邻接表中遍历其相连的边结点,边结点个数即为度

有向图:邻接矩阵中出度遍历行,入度遍历列,非零元素个数即为度;邻接表中出度遍历该节点的边结点,边结点个数即为出度;入度需遍历全部边结点

21ef3d6ba5c74dbfb55c0c5e4f95748c.png

#define MaxVertexNum 100
typedef struct ArcNode{
    struct ArcNode *next;
    int adjvex;
}ArcNode;
typedef struct VNode{
    elemtype data;
    ArcNode *first;
}VNode, AdjList[MaxVertexNum];
typedef struct Grpah{
    AdjList vertices;
    int vexNum, arcNum;
}Graph
void Convert(Graph &G, int arr[][]){
    for (int i = 0; i < G.vexNum; i++){
        //找到第一个结点
        ArcNode *p = (G->vertices[i]).first;
        //循环遍历下一个结点
        while(p){
            //将第i行p->adjvex列置为1
            arr[i][p->adjvex] = 1;
            p = p->next;
        }
    }
}

d82e775b57eb47a8afcad71ba4471992.png

int IsExitEl(MGraph G){
    int degree;
    int n = 0;
    //遍历图的各个顶点
    for (int i = 0; i < G.numVertices; i++){
        //度清零
        degree = 0;
        //二维数组的当前元素值不等于0,则度数+1
        for (int j = 0; j < G.numVertices; j++) if (Edge[i][j]) degree++;
        //度数为奇数,则个数+1
        if (degree % 2) n++;
    }//for
    //个数为0或者2,return 1;否则,return 0
    if (n == 0 || n == 2) return 1;
    else return false;
}


相关文章
|
9月前
|
存储 算法 C++
【C++数据结构——图】图的邻接矩阵和邻接表的存储(头歌实践教学平台习题)【合集】
本任务要求编写程序实现图的邻接矩阵和邻接表的存储。需掌握带权有向图、图的邻接矩阵及邻接表的概念。邻接矩阵用于表示顶点间的连接关系,邻接表则通过链表结构存储图信息。测试输入为图的顶点数、边数及邻接矩阵,预期输出为Prim算法求解结果。通关代码提供了完整的C++实现,包括输入、构建和打印邻接矩阵与邻接表的功能。
288 10
|
存储 JSON NoSQL
redis基本数据结构(String,Hash,Set,List,SortedSet)【学习笔记】
这篇文章是关于Redis基本数据结构的学习笔记,包括了String、Hash、Set、List和SortedSet的介绍和常用命令。文章解释了每种数据结构的特点和使用场景,并通过命令示例演示了如何在Redis中操作这些数据结构。此外,还提供了一些练习示例,帮助读者更好地理解和应用这些数据结构。
redis基本数据结构(String,Hash,Set,List,SortedSet)【学习笔记】
java数据结构,线性表链式存储(单链表)的实现
文章讲解了单链表的基本概念和Java实现,包括头指针、尾节点和节点结构。提供了实现代码,包括数据结构、接口定义和具体实现类。通过测试代码演示了单链表的基本操作,如添加、删除、更新和查找元素,并总结了操作的时间复杂度。
java数据结构,线性表链式存储(单链表)的实现
|
存储 安全 数据库
除了 HashMap,还有哪些数据结构可以实现键值对存储?
【10月更文挑战第11天】 除了`HashMap`,其他常见支持键值对存储的数据结构包括:`TreeMap`(基于红黑树,键有序)、`LinkedHashMap`(保留插入顺序)、`HashTable`(线程安全)、`B-Tree`和`B+Tree`(高效存储大量数据)、`SkipList`(通过跳跃指针提高查找效率)及`UnorderedMap`(类似`HashMap`)。选择合适的数据结构需根据排序、并发、存储和查找性能等需求。
|
存储 人工智能 C语言
数据结构基础详解(C语言): 栈的括号匹配(实战)与栈的表达式求值&&特殊矩阵的压缩存储
本文首先介绍了栈的应用之一——括号匹配,利用栈的特性实现左右括号的匹配检测。接着详细描述了南京理工大学的一道编程题,要求判断输入字符串中的括号是否正确匹配,并给出了完整的代码示例。此外,还探讨了栈在表达式求值中的应用,包括中缀、后缀和前缀表达式的转换与计算方法。最后,文章介绍了矩阵的压缩存储技术,涵盖对称矩阵、三角矩阵及稀疏矩阵的不同压缩存储策略,提高存储效率。
860 9
|
存储 算法 C语言
数据结构基础详解(C语言): 二叉树的遍历_线索二叉树_树的存储结构_树与森林详解
本文从二叉树遍历入手,详细介绍了先序、中序和后序遍历方法,并探讨了如何构建二叉树及线索二叉树的概念。接着,文章讲解了树和森林的存储结构,特别是如何将树与森林转换为二叉树形式,以便利用二叉树的遍历方法。最后,讨论了树和森林的遍历算法,包括先根、后根和层次遍历。通过这些内容,读者可以全面了解二叉树及其相关概念。
259 6
|
存储 机器学习/深度学习 C语言
数据结构基础详解(C语言): 树与二叉树的基本类型与存储结构详解
本文介绍了树和二叉树的基本概念及性质。树是由节点组成的层次结构,其中节点的度为其分支数量,树的度为树中最大节点度数。二叉树是一种特殊的树,其节点最多有两个子节点,具有多种性质,如叶子节点数与度为2的节点数之间的关系。此外,还介绍了二叉树的不同形态,包括满二叉树、完全二叉树、二叉排序树和平衡二叉树,并探讨了二叉树的顺序存储和链式存储结构。
221 3
|
存储 算法 C语言
C语言手撕数据结构代码_顺序表_静态存储_动态存储
本文介绍了基于静态和动态存储的顺序表操作实现,涵盖创建、删除、插入、合并、求交集与差集、逆置及循环移动等常见操作。通过详细的C语言代码示例,展示了如何高效地处理顺序表数据结构的各种问题。
164 1
|
存储 Java
数据结构中的哈希表(java实现)利用哈希表实现学生信息的存储
这篇文章通过Java代码示例展示了如何实现哈希表,包括定义结点类、链表类、数组存储多条链表,并使用简单的散列函数处理冲突,以及如何利用哈希表存储和查询学生信息。
数据结构中的哈希表(java实现)利用哈希表实现学生信息的存储
java数据结构,线性表顺序存储(数组)的实现
文章介绍了Java中线性表顺序存储(数组)的实现。线性表是数据结构的一种,它使用数组来实现。文章详细描述了线性表的基本操作,如增加、查找、删除、修改元素,以及其他操作如遍历、清空、求长度等。同时,提供了完整的Java代码实现,包括MyList接口和MyLinearList实现类。通过main函数的测试代码,展示了如何使用这些方法操作线性表。

热门文章

最新文章