有向图和无向图的表示方式(邻接矩阵,邻接表)

目录

一.邻接矩阵

1.无向图​编辑

2.有向图

补充:网(有权图)的邻接矩阵表示法

二.邻接表

1.无向图

2.有向图

三.邻接矩阵与邻接表的关系


一.邻接矩阵

1.无向图

(1)对角线上是每一个顶点与自身之间的关系,没有到自身的边,所以对角线上为0

(2)无向图的邻接矩阵是对称的

两个顶点之间如果有边的话,那么两个顶点互为邻接关系,值为1

(3)顶点i的度=第i行(列)中1的个数

注:完全图的邻接矩阵,对角元素为0,其余为1

2.有向图

(1)在有向图的邻接矩阵中

第i行含义:以结点v_{i}为尾的弧(即出度边)

顶点的出度=第i行元素之和

第i列含义:以结点v_{i}为头的弧(即入度边)

顶点的入度=第i列元素之和

顶点的度=第i行元素之和+第i列元素之和

(2)有向图的邻接矩阵可能是不对称的

补充:网(有权图)的邻接矩阵表示法

邻接矩阵存储

#define MaxInt 32767
#define MVNum 100    //最大顶点数
typedef char VerTexType;    //设顶点的数据类型为字符型
typedef int ArcType;    //假设边的权值类型为整型typedef struct{VerTex vex[MVNum];    //顶点表ArcType arcs[MVNum][MVNum];    //邻接矩阵int vexnum,arcnum;    //图的当前点数和边数
}AMGraph;

 以无向网为例

int LocateVex(AMGraph G,VertexType u)
{
//查找图G中的顶点u,存在则返回顶点表中的下标;否则返回-1int i;for(i=0;i<G.vexnum;++i)//有几条边就循环多少次{if(u==G.vexs[i])return i;return -1;}
}Status CreateUDN(AMGraph &G)
{int i;cin>>G.vexnum>>G.arcnum;//总顶点,总边数for(i=0;i<G.vexnum;++i)cin>>G.vexs[i];//依次输入点的信息for(i=0;i<G.vexnum;++i)//初始化邻接矩阵{for(int j=0;j<G.vexnum;++j){G.arcs[i][j]=MaxInt;//边的权值均置为极大值}}for(int k=0;k<G.arcnum;++k)//构造邻接矩阵{cin>>v1>>v2>>w;//输入一条边所依附的顶点以及边的权值i=LocateVex(G,v1);j=LocateVex(G,v2);//确定v1,v2在G中的位置G.arcs[i][j]=w;//边<v1,v2>的权值置wG.arcs[j][i]=G.arcs[i][j];//<v1,v2>的对称边<v2,v1>的权值也为w}return OK;
}

无向图,有向网,有向图与无向网是类似的

•对于无向图而言,其与无向网相比没有权值

初始化邻接矩阵时,w=0 ,构建邻接矩阵时,w=1

Status CreateUDG(AMGraph &G)
{int i;cin>>G.vexnum>>G.arcnum;//总顶点,总边数for(i=0;i<G.vexnum;++i)cin>>G.vexs[i];//依次输入点的信息for(i=0;i<G.vexnum;++i)//初始化邻接矩阵{for(int j=0;j<G.vexnum;++j){G.arcs[i][j]=0;//边的权值均置为0}}for(int k=0;k<G.arcnum;++k)//构造邻接矩阵{cin>>v1>>v2;//输入一条边所依附的顶点int w=1;//1表示连接、0表示无连接i=LocateVex(G,v1);j=LocateVex(G,v2);//确定v1,v2在G中的位置G.arcs[i][j]=w;//边<v1,v2>的权值置wG.arcs[j][i]=G.arcs[i][j];//<v1,v2>的对称边<v2,v1>的权值也为w}return OK;
}

•对于有向网而言,与无向网不同的是,其每一条弧,都是从一个顶点指向另外一个顶点的

仅为G.arcs[i][j]赋值,不为G.arcs[j][i]赋值

Status CreateDN(AMGraph &G)
{int i;cin>>G.vexnum>>G.arcnum;//总顶点,总边数for(i=0;i<G.vexnum;++i)cin>>G.vexs[i];//依次输入点的信息for(i=0;i<G.vexnum;++i)//初始化邻接矩阵{for(int j=0;j<G.vexnum;++j){G.arcs[i][j]=MaxInt;//边的权值均置为极大值}}for(int k=0;k<G.arcnum;++k)//构造邻接矩阵{cin>>v1>>v2>>w;//输入一条边所依附的顶点以及边的权值i=LocateVex(G,v1);j=LocateVex(G,v2);//确定v1,v2在G中的位置G.arcs[i][j]=w;//边<v1,v2>的权值置w}return OK;
}

•对于有向图而言,只需要将无向图和有向网的修改结合一下就行

没有权值,连接两个顶点的边是弧 

Status CreateDG(AMGraph &G)
{int i;cin>>G.vexnum>>G.arcnum;//总顶点,总边数for(i=0;i<G.vexnum;++i)cin>>G.vexs[i];//依次输入点的信息for(i=0;i<G.vexnum;++i)//初始化邻接矩阵{for(int j=0;j<G.vexnum;++j){G.arcs[i][j]=0;//边的权值均置为0}}for(int k=0;k<G.arcnum;++k)//构造邻接矩阵{cin>>v1>>v2;//输入一条边所依附的顶点int w=1;//1表示连接、0表示无连接i=LocateVex(G,v1);j=LocateVex(G,v2);//确定v1,v2在G中的位置G.arcs[i][j]=w;//边<v1,v2>的权值置w}return OK;
}

邻接矩阵的优点

 

•方便检查任意一对顶点间是否存在边

•方便找任一顶点的所有“邻接点”(有边直接相连的顶点)

•方便计算任一顶点的“度”(从该点发出的边数为“出度”,指向该点的边数为“入度”)

        •无向图:对应行 (或列)非0元素的个数

        •有向图:对应行非0元素的个数是“出度”;对应列非0元素的个数是“入度

邻接矩阵的缺点

•不便于增加和删除顶点

•邻接矩阵的空间复杂度为O(n^{2}),跟其有的边的条数无关,只与其顶点数有关,无论边少还是边多,空间复杂度都为O(n^{2}),浪费空间----存稀疏图(点很多而边很少)有大量无效元素

•浪费时间----统计稀疏图中一共有多少条边,因为必须遍历所有元素

二.邻接表

1.无向图

顶点:按编号顺序存储在一维数组中

这里的一维数组和邻接矩阵中的一维数组不同,数组中每个元素有两个成员

第一个是数据元素的信息,第二个是指针,存储的是第一个边的地址

关联同一顶点的边:用线性链表存储,例如3,表示邻接的顶点是下标为3的元素(v4)

如果有边\弧的信息,还可以在表结点中增加一项

第一个表示邻接点在顶点表中的序号

第二个元素是一个指针,指向的是下一条边(弧)

第三个元素表示边的信息(权值)

(1)邻接表是不唯一

例如“v1”指针指向的是邻接点v4和v2的下标,分别为3,1,这些边的顺序是可以改变的。

(2)若无向图中有n个顶点,e条边,则其邻接表需n个头结点和2e个表结点,适宜存储稀疏图

使用每条边时会出现两次,从v1到v2和从v2到v1用的是同一条边,所以有e条边,就有2e个表结点

所以无向图的存储空间为O(n+2e):n表示点,2e表示边

有向图的存储空间为O(n+e)

注对于邻接矩阵而言,存储空间为O(n^{2}),所以邻接表在存储稀疏图时比较节省空间

 (3)无向图中顶点v_{i}的度为第i个单链表中的结点数

顶点的存储结构

typedef struct VNode
{VerTexType data;         //顶点信息ArcNode *firstarc;       //指向第一条依附该顶点的边的指针
}VNode,AdjList[MVNum];
注:AdjList[MVNum]==VNnode v[MVNum]

•弧(边)的结点结构

#define MVNum 100                //最大顶点数
typedef struct ArcNode           //边结点
{int adjvex;                  //该边所指向的顶点的位置struct ArcNode *nextarc;     //指向下一条边的指针OtherInfo info;              //和边相关的信息(权值等)
}ArcNode;

•图的结构定义

typedef struct
{AdjList vertices;      //存放各个顶点的数组int vexnum, arcnum;    //图的当前顶点数和弧数
}ALGraph;

邻接表操作举例说明

ALGraph G;                    //定义了邻接表表示的图G
G.vexnum = 5; G.arcnum = 6;   //图G中包含5个顶点,6条边
G.vertices[1].data = 'b';     //图G中的第2个顶点是b
p = G.vertices[1].firstarc;   //指针p指向顶点b的第一条边结点
p->adjvex = 4;                //指针p所指边结点是到下标为4的结点的边

2.有向图

(1)顶点v_{i}出度为第i个单链表中的结点个数

(2)顶点v_{i}入度为整个单链表中邻接点域值是(i-1)的结点个数

根据以上结论,可以看出对于这样的每一个顶点存储出度边的有向图而言,找出度是容易的,找入度则比较难,例如,找终点为v1的边,那么就需要遍历所有边结点,找到邻结点为0的入度边

 也可以每一个顶点存储其入度边,如下图:逆邻接表

和邻接表的结论相反:找入度容易,找出度难

(1)顶点v_{i}入度为第i个单链表中的结点个数

(2)顶点v_{i}出度为整个单链表中邻接点域值是(i-1)的结点个数

例题:画出该邻接表对应的网络图

结果如下


 

用邻接表创建无向图

int LocateVex(AMGraph G,VertexType u)
{
//查找图G中的顶点u,存在则返回顶点表中的下标;否则返回-1int i;for(i=0;i<G.vexnum;++i)//有几条边就循环多少次{if(u==G.vexs[i])return i;return -1;}
}Status CreateUDG(ALGraph &G)
{int i, j, k;cin >> G.vexnum >> G.arcnum;                            // 输入总顶点数,总边数for (i = 0; i < G.vexnum; ++i)                          // 输入各点,构造表头(顶点)节点表{cin >> G.vertices[i].data;                          // 输入顶点值G.vertices[i].firstarc = NULL;                      // 初始化表头结点的指针域}for (k = 0; k < G.arcnum; ++k)                          // 输入各边,构造邻接表{int v1, v2;cin >> v1 >> v2;                                    // 输入一条边依附的两个顶点i = LocateVex(G, v1);j = LocateVex(G, v2);ArcNode* p1 = new ArcNode;                           // 生成一个新的边结点*p1p1->adjvex = j;                                     // 邻接点序号为jp1->nextarc = G.vertices[i].firstarc;G.vertices[i].firstarc = p1;                         // 将新结点*p1插入顶点vi的边表头部(头插法)ArcNode* p2 = new ArcNode;                           // 生成一个新的边结点*p2p2->adjvex = i;                                     // 邻接点序号为ip2->nextarc = G.vertices[j].firstarc;G.vertices[j].firstarc = p2;                         // 将新结点*p2插入顶点vj的边表头部(头插法)}return OK;
}

这里的头插法特别解释一下

p1->nextarc = G.vertices[i].firstarc;  
G.vertices[i].firstarc = p1;           //将新结点*p1插入顶点vi的边表头部(头插法)

用邻接表创建有向图

只需将边改为弧,将以下代码去掉

        p2 = new ArcNode;                      //生成一个新的边结点*p2
        p2->adjvex = i;                        //邻接点序号为i
        p2->nextarc = G.vertices[j].firstarc;
        G.vertices[j].firstarc = p2;           //将新结点*p2插入顶点vj的边表头部(头插法)

Status CreateDG(ALGraph &G)
{cin >> G.vexnum >> G.arcnum;                        // 输入总顶点数,总边数for (int i = 0; i < G.vexnum; ++i)                   // 输入各点,构造表头(顶点)节点表{cin >> G.vertices[i].data;                      // 输入顶点值G.vertices[i].firstarc = NULL;                  // 初始化表头结点的指针域}for (int k = 0; k < G.arcnum; ++k)                   // 输入各边,构造邻接表{int v1, v2;cin >> v1 >> v2;                                 // 输入一条边依附的两个顶点int i = LocateVex(G, v1);int j = LocateVex(G, v2);ArcNode* p = new ArcNode;                         // 生成一个新的边结点*pp->adjvex = j;                                   // 邻接点序号为jp->nextarc = G.vertices[i].firstarc;             G.vertices[i].firstarc = p;                      // 将新结点*p插入顶点vi的边表头部(头插法)}return OK;
}

 用邻接表创建有向网

只需加入weight(权重值即可)

cin >> v1 >> v2 >> weight;

p->info=weight;

Status CreateWeightedDN(ALGraph &G)
{cin >> G.vexnum >> G.arcnum;                        // 输入总顶点数,总边数for (int i = 0; i < G.vexnum; ++i)                   // 输入各点,构造表头(顶点)节点表{cin >> G.vertices[i].data;                      // 输入顶点值G.vertices[i].firstarc = NULL;                  // 初始化表头结点的指针域}for (int k = 0; k < G.arcnum; ++k)                   // 输入各边,构造邻接表{int v1, v2, weight;cin >> v1 >> v2 >> weight;                       // 输入一条边依附的两个顶点和权值int i = LocateVex(G, v1);int j = LocateVex(G, v2);ArcNode* p = new ArcNode;                         // 生成一个新的边结点*pp->adjvex = j;                                   // 邻接点序号为jp->info = weight;                              // 边的权值为weightp->nextarc = G.vertices[i].firstarc;             G.vertices[i].firstarc = p;                      // 将新结点*p插入顶点vi的边表头部(头插法)}return OK;
}

 用邻接表创建无向网

只需在无向图的基础上加入weight(权重值即可)

cin >> v1 >> v2 >> weight;

p1->info=weight;

p2->info=weight;

Status CreateWeightedUDN(ALGraph &G)
{cin >> G.vexnum >> G.arcnum;                            // 输入总顶点数,总边数for (int i = 0; i < G.vexnum; ++i)                       // 输入各点,构造表头(顶点)节点表{cin >> G.vertices[i].data;                          // 输入顶点值G.vertices[i].firstarc = NULL;                      // 初始化表头结点的指针域}for (int k = 0; k < G.arcnum; ++k)                       // 输入各边,构造邻接表{int v1, v2, weight;cin >> v1 >> v2 >> weight;                           // 输入一条边依附的两个顶点和权值int i = LocateVex(G, v1);int j = LocateVex(G, v2);ArcNode* p1 = new ArcNode;                            // 生成一个新的边结点*p1p1->adjvex = j;                                      // 邻接点序号为jp1->info = weight;                                 // 边的权值为weightp1->nextarc = G.vertices[i].firstarc;G.vertices[i].firstarc = p1;                         // 将新结点*p1插入顶点vi的边表头部(头插法)ArcNode* p2 = new ArcNode;                            // 生成一个新的边结点*p2p2->adjvex = i;                                      // 邻接点序号为ip2->info = weight;                                 // 边的权值为weightp2->nextarc = G.vertices[j].firstarc;G.vertices[j].firstarc = p2;                         // 将新结点*p2插入顶点vj的边表头部(头插法)}return OK;
}

 邻接表的特点

•方便找任一顶点的所有“邻接点”
•节约稀疏图的空间
        •需要N个头指针 + 2E个结点 (每个结点至少2个域)

•方便计算任一顶点的“度”
对无向图:是的
对有向图:只能计算“出度”需要构造"逆邻接表"(存指向自己的边)来方便计算"入度"

•不方便检查任意一对顶点间是否存在边

三.邻接矩阵与邻接表的关系

1.联系:邻接表中每个链表对应于邻接矩阵中的一行,链表中结点个数等于一行中非零元素的个数

2.区别:
①对于任一确定的无向图,邻接矩阵是唯一的 (行列号与顶点编号致),但邻接表不唯一 (链接次序与顶点编号无关,与链接的算法有关(头插法或尾插法))

②邻接矩阵的空间复杂度为O(n^{2},而邻接表的空间复杂度为O(n+e),对于稀疏图而言,用邻接表的方式存储,空间复杂度更低。

3.用途:邻接矩阵多用于稠密图,邻接表多用于稀疏图。

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

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

相关文章

数据库(MySQL)的存储过程

一、存储过程介绍 存储过程是事先经过编译并存储在数据库中的一段SQL 语句的集合&#xff0c;调用存储过程可以简化应用开发人员的很多工作&#xff0c;减少数据在数据库和应用服务器之间的传输&#xff0c;对于提高数据处理的效率是有好处的。 存储过程思想上很简单&#xff0…

多功能透明屏,在智能家居领域中,有哪些功能特点?显示、连接

多功能透明屏是一种新型的显示技术&#xff0c;它能够在透明的表面上显示图像和视频&#xff0c;并且具有多种功能。 这种屏幕可以应用于各种领域&#xff0c;如商业广告、智能家居、教育等&#xff0c;为用户提供更加便捷和多样化的体验。 首先&#xff0c;多功能透明屏可以…

[HNCTF 2022 Week1]——Web方向 详细Writeup

Week1 [HNCTF 2022 Week1]2048 f12查看源代码 可以看出游戏的分数是score 修改score的值 得到flag [HNCTF 2022 Week1]Interesting_include 得到源码 <?php //WEB手要懂得搜索 //flag in ./flag.phpif(isset($_GET[filter])){$file $_GET[filter];if(!preg_match(&qu…

3次多项式轨迹规划(PLC SCL代码)

机器人、运动控制等常用的轨迹规划有三次多项式、五次多项式、梯形速度规划,S型速度规划,今天我们主要介绍三次多项式轨迹规划,有关T型和S型轨迹规划大家可以查看下面文章博客,这里不再赘述, 梯形轨迹规划 梯形速度曲线轨迹规划(速度前馈+PID、SCL+ ST代码)_RXXW_Dor的博…

雅思写作 三小时浓缩学习顾家北 笔记总结(五)

目录 饥饿网100句翻译练习 Many girls are unwilling to seek employment in male-dominated industries. Many girls are not willing to find jobs in male-dominated industries. The main function of schools is to impart knowledge to the next generation. The ar…

Kafka3.0.0版本——增加副本因子

目录 一、服务器信息二、启动zookeeper和kafka集群2.1、先启动zookeeper集群2.2、再启动kafka集群 三、增加副本因子3.1、增加副本因子的概述3.2、增加副本因子的示例3.2.1、创建topic(主题)3.2.2、手动增加副本存储 一、服务器信息 四台服务器 原始服务器名称原始服务器ip节点…

PostgreSQL PG15 新功能 PG_WALINSPECT

开头还是介绍一下群&#xff0c;如果感兴趣PolarDB ,MongoDB ,MySQL ,PostgreSQL ,Redis &#xff0c;Oracle ,Oceanbase 等有问题&#xff0c;有需求都可以加群群内有各大数据库行业大咖&#xff0c;CTO&#xff0c;可以解决你的问题。加群请加微信号 liuaustin3 &#xff08;…

Android离线文字识别-tesseract4android调用

Android在线文字识别可以调阿里云的接口Android文字识别-阿里云OCR调用__花花的博客-CSDN博客 需要离线文字识别的话&#xff0c;可以调tesseract4android。个人测试效果不是特别理想&#xff0c;但是速度真的很快&#xff0c;VIVO S10后摄照片&#xff0c;80ms内识别完成。现…

qemu-system-x86_64 命令创建虚拟机,报gtk initialization failed的

因为是ssh命令行启动&#xff0c;增加--nographic # /opt/debug/bin/qemu-system-aarch64 -machine virt-6.2 -qmp tcp:localhost:1238,server,nowait --nographic configure accelerator virt-6.2 start machine init start cpu init start add rom file: virtio-net-pci…

【免费模板】2023数学建模国赛word+latex模板免费分享

无需转发 免费获取2023国赛模板&#xff0c;获取方式见文末 模板文件预览如下&#xff1a; 模板参考格式如下&#xff1a; &#xff08;题目&#xff09;XXXXXX 摘 要&#xff1a; 开头段&#xff1a;需要充分概括论文内容&#xff0c;一般两到三句话即可&#xff0c;长度控…

idea的git入门

&#xff08;1&#xff09;安装好git之后&#xff0c;在idea的设置里面&#xff0c;按照下面三步&#xff0c;配置git &#xff08;2&#xff09;创建本地git仓库 选择本地仓库的根目录&#xff0c;点击ok &#xff08;3&#xff09;创建成功之后&#xff0c;会发现文件名称都变…

Notepad++ 的安装及配置

由于电脑重装了Win11系统&#xff0c;干脆重头开始&#xff0c;重新安装每一个软件~~~ 很多博客或者博主都会推荐notepad的官网&#xff1a;https://notepad-plus-plus.org/ 但大家亲自点开就会发现是无响应&#xff0c;如下图 同时&#xff0c;也会有很多博主直接给网盘地址…

垃圾回收 - 分代垃圾回收

分代垃圾回收在对象中导入了“年龄”的概念&#xff0c;通过优先回收容易成为垃圾的对象&#xff0c;提高垃圾回收的效率。 1、新生代对象和老年代对象 分代垃圾回收中把对象分类成几代&#xff0c;针对不同的代使用不同的 GC 算法&#xff0c;我们把刚生成的对象称为新生代对…

三维数字沙盘电子沙盘虚拟现实模拟推演大数据人工智能开发教程第15课

三维数字沙盘电子沙盘虚拟现实模拟推演大数据人工智能开发教程第15课 现在不管什么GIS平台首先要解决的就是数据来源问题&#xff0c;因为没有数据的GIS就是一个空壳&#xff0c;下面我就目前一些主流的数据获取 方式了解做如下之我见&#xff08;主要针对互联网上的一些卫星…

java+ssm+mysql电梯管理系统

项目介绍&#xff1a; 使用javassmmysql开发的电梯管理系统&#xff0c;系统包含管理员&#xff0c;监管员、安全员、维保员角色&#xff0c;功能如下&#xff1a; 管理员&#xff1a;系统用户管理&#xff08;监管员、安全员、维保员&#xff09;&#xff1b;系统公告&#…

Android签名查看

查看签名文件信息 第一种方法&#xff1a; 1.打开cmd&#xff0c;执行keytool -list -v -keystore xxx.keystore&#xff0c;效果如下图&#xff1a; 第二种方法: 1.打开cmd&#xff0c;执行 keytool -list -v -keystore xxxx.keystore -storepass 签名文件密码&#xff0…

Python调试学习资料

Python调试学习资料 python -m pdb example.py网络资源 Python代码调试的几种方法总结Python 程序如何高效地调试&#xff1f;Python Debugging With Pdbpdb — The Python DebuggerThe Python Debugger (pdb)Python Debugger with ExamplesHow to port Python 2 Code to Pyth…

功率信号源可以应用在哪些方面

功率信号源是一种能够产生一定功率的信号源&#xff0c;广泛应用于各个领域。下面将介绍功率信号源在电子、通信、工业和科研等方面的应用。 在电子行业中&#xff0c;功率信号源是一种重要的测试工具。它可以产生各种波形的信号&#xff0c;如正弦波、方波、脉冲波等&#xff…

[Java]异常

目录 1.异常的概念与体系结构 1.1异常的概念 1.1.1算术异常 1.1.2数组越界异常 1.1.3空指针异常 1.2异常的体系结构 1.3异常的分类 2.异常的处理 2.1 防御式编程 2.2异常的抛出 2.3异常的捕获 2.3.1 异常声明throws 将光标放在抛出异常方法上&#xff0c;alt Insert …

合宙Air724UG LuatOS-Air LVGL API控件--下拉框 (Dropdown)

下拉框 (Dropdown) 在显示选项过多时&#xff0c;可以通过下拉框收起多余选项。只为用户展示列表中的一项。 示例代码 -- 回调函数 event_handler function(obj, event)if (event lvgl.EVENT_VALUE_CHANGED) thenprint("Option:", lvgl.dropdown_get_symbol(obj)…