noj数据结构稀疏矩阵的加法十字链表_数据结构之:图

6127d57f9a149d3ea6b44deac18bb457.png

导读

1. 什么是图?图的存储方式?

2. 图的遍历(深度优先搜索,广度优先搜索)

3. 最短路径

1. 什么是图?图的存储方式?

  前面总结了“树”这种数据结构,而这篇博客总结的是更为复杂的一种数据结构:图(graph),它表明了物件与物件之间的“多对多”的一种复杂关系。图包含了两个基本元素:顶点(vertex, 简称V)和边(edge,简称E)。

有向图与无向图

  如果给图的每条边规定一个方向,那么得到的图称为有向图。在有向图中,从一个顶点出发的边数称为该点的出度,而指向一个顶点的边数称为该点的入度。相反,边没有方向的图称为无向图

有权图与无权图

  如果图中的边有各自的权重,得到的图是有权图。比如地铁路线图,连接两站的边的权重可以是距离,也可以是价格,或者其他。反之,如果图的边没有权重,或者权重都一样(即没有区分),称为无权图

连通图

  如果图中任意两点都是连通的,那么图被称作连通图。图的连通性是图的基本性质。无向图中的一个极大连通子图称为其的一个连通分量。有向图中,如果对任意两个顶点

都存在
以及
的路径,则称其为
强连通图,对应有强连通分量的概念。

图的存储

  常用的存储方式有两种:邻接矩阵和邻接表。

邻接矩阵

  采用一个大小为

的矩阵
,对于有权图,
可以表示
的边的权重,如果是无权图,则可设为1表示存在边,0表示不存在边。因此邻接矩阵的表示相当的直观,而且对于查找某一条边是否存在、权重多少非常快。但其比较浪费空间,对稠密图(
)来说,会比较适合。

  一般情况下我用的邻接矩阵的结构和初始化代码如下,具体会根据使用需求有所改动。

struct GNode{int Nv; // number of verticesint Ne; // number of edgesint G[MaxVNum][MaxVNum];
};
typedef struct GNode *MGraph;
MGraph Graph = new GNode[MaxVNum];//  初始化图
void InitGraph(int N,int E){Graph->Nv = N;Graph->Ne = E;for (int u=0;u<=N;u++)for (int v=0;v<=N;v++)Graph->G[u][v]=INF;
}
//插入边(这里是有权重的无向边)
void InsertEdge(int u,int v,int weight){Graph->G[u][v]= weight;Graph->G[v][u]=weight;
}

邻接表

为一个大小为
的指针数组, 对应每行存储一个链表,如
中存储从顶点
出发的边,如从顶点1出发的边有
其在
中存储的形式如下图所示。邻接表比较适合稀疏图(

002005e9e9176c102f43acf402f38017.png
邻接表存储图。

  另外,之前做习题的时候也会习惯用个二维数组来存储邻接表,比如上面的

  一般情况下我用的代码如下,会根据具体需求有大改动。

typedef struct VNode *AdjNode;
struct VNode{int node;AdjNode next;
};
AdjNode Graph = new VNode[MaxV];void InitGraph(int N,int E){Graph->Nv = N;Graph->Ne = E;for (int i=1;i<=N;i++){(Graph+i)->node=i;(Graph+i)->next=NULL;}
}
// 插入有向边
void InsertEdge(int u,int v){AdjNode tmp1=new VNode;AdjNode tmp2 = new VNode;tmp1->node = v;tmp1->next = (Graph+u)->next;(Graph+u)->next=tmp1;tmp2->node = u;tmp2->next = (Graph+v)->next;(Graph+v)->next=tmp2;
}

2. 图的遍历

  图的遍历最常用的有两种:深度优先搜索(Depth-first Search, DFS)和广度优先搜索(Breadth-First Search, BFS)。

深度优先搜索

有点类似于树的前序遍历,即从一个选定的点出发,选定与其直接相连且未被访问过的点走过去,然后再从这个当前点,找与其直接相连且未被访问过的点访问,每次访问的点都标记为“已访问”,就这么一条道走到黑,直到没法再走为止。没法再走怎么办呢?从当前点退回其“来处”的点,看是否存在与这个点直接相连且未被访问的点。重复上述步骤,直到没有未被访问的点为止。

  从上述文字表述可以看出,DFS很适合使用递归,其代码如下:

int visited[MaxN]={0}; //记录顶点的访问状态
int result[MaxN]; //result数组记录DFS遍历结果
int N,E,k;// N为顶点数,E为边数,k记录遍历结果下标void DFS(int v){visited[v]=1;result[k++]=v;for (int i=0;i<N;i++){if (G[v][i]==1 && visited[i]==0)DFS(i);}
}int main(){for (int i=0;i<N;i++){k = 0;if (visited[i]==0){DFS(i);//用{}打印出一个连通分量cout<<"{ ";for (int j=0;j<k;j++)cout<<result[j]<<' ';cout<<"}"<<endl;}}
return 0;
}

广度优先搜索

有点类似于树的层序遍历,也就是像剥洋葱一样,“一层一层地剥开♩♩♩”。即从一个选定的点出发,将与其直接相连的点都收入囊中,然后依次对这些点去收与其直接相连的点。重复到所有点都被访问然后结束。

  类似树的层序遍历,BFS同样可以通过一个队列来实现,代码如下:

int visited[MaxN]={0}; //记录顶点的访问状态
int result[MaxN]; //result数组记录BFS遍历结果
int N,E,k;// N为顶点数,E为边数,k记录遍历结果下标void BFS(int v){queue<int> q;q.push(v);visited[v]=1;result[k++]=v;while(!q.empty()){int u = q.front();q.pop();for(int i=0;i<N;i++){if (G[u][i]!=0 && visited[i]!=1){visited[i]=1;q.push(i);result[k++]=i;}}}
}int main(){for (int i=0;i<N;i++){k = 0;if (visited2[i]==0){BFS(i);//用{}打印出一个连通分量cout<<"{ ";for (int j=0;j<k;j++)cout<<result[j]<<' ';cout<<"}"<<endl;}}
return 0;
}
上述BFS和DFS的时间复杂度均为

3. 最短路径

  简而言之,最短路径就是找图中连接两个顶点所有边权重和最小的路径。

  • 对无权图而言,即是找边最小的路径;
  • 如果给定起点,则是单源最短路径,即从一固定起点到任意终点的最短路径;
  • 如果起点不确定,则是多源最短路径,即求任意两个顶点的最短路径。

单源最短路径

  对于无向图而言,可以借助BFS的“剥洋葱”特性,看从起点到终点需要剥几个洋葱圈,剥的层数即是最短路径长度。

  对于有向图而言,就不得不提到一个煊赫的名字:Dijkstra算法。陆续泛听了几个版本的数据结构,这个名字简直太深入人心。

  • Dijkstra算法

  具体来说Dijkstra算法的核心在于:从起点(或者说源点)开始,将其装进一个“袋子”里,然后不断往这个袋子里搜罗顶点,当顶点收进去后,能保证从源点到该顶点的当前最短路径是确定的。每次收录的顶点是在未收录的集合里寻找最短路径最小的点(即离源点最近的点),然后将与收进去的顶点直接相连的点的最短路径进行更新。

  如下图所示,

是新鲜被收录的点,
等是和
有直接相连的边的点,那么这些点的距离会在
收录后立马被更新。

  值得注意的是:Dijkstra算法不适用于存在负权重边的图。

45019c9eb1e062ad12ed06edea2a1e50.png

  给出Dijkstra代码前先对保存最短路径的数组dist[]和收录状态的数组collected[]进行初始化:和源点直接相连的点的最短路径即为该边的权重,其余均初始化为一个很大的(一看就不可能的数)INF;collected中所有值初始化为false。

int dist[MaxN];
bool collected[MaxN];void InitDist(int start){for (int i=0;i<Graph->Nv;i++){dist[i]=Graph->G[start][i];collected[i]=false;}
}

  然后,我们需要寻找下一步被收录的点:即在所有未被收录的点中寻找dist最小的点。如果不存在(最小值为无穷大INF),则返回错误标识-1。这一步找最小值,我们可以每次都将所有顶点扫描一遍获得,也可以很自然地想到将dist存在一个最小堆(优先队列)里。对于前者,因为每次收录都要扫描一次所有顶点,有

复杂度,然后每条边都会扫描一次,时间复杂度为
。对于后者,每次收录时找到目标的时间为
,有
复杂度,而每次扫描边然后更新dist的时间也是
,有
复杂度,所以总的复杂度为
int findMinDist(int start){int MinD = INF;int MinV;for (int end =0;end<Graph->Nv;end++){if (collected[end]==false && dist[end]<MinD){MinD=dist[end];MinV=end;}}if (MinD<INF)return MinV;elsereturn -1;
}

  然后是Dijkstra算法主程序:在初始化后,先将源点收录进袋,并且源点的最短路径设为0, 然后开始循环寻找能被收录的点:如果接受到的是错误标识-1,说明找不到符合要求的点了,程序结束。如果找到了,则先将这个点收录进去,记录为s; 然后遍历从s出发的所有边,对没有收录的点,更新他们的最小路径值。更新的依据是:如果这个点t的当前的最小路径值,大于s点的最小路径值与s到t边的权重值之和,则更新为

,否则维持原状。
bool dijkstra(int start){InitDist(start);dist[start]=0;collected[start]=true;int s;while (1){s = findMinDist(start);if (s==-1)break;collected[s]=true;for (int t=0;t<Graph->Nv;t++){// if W is not collected and s->t existsif (collected[t]==false && Graph->G[s][t]<INF){// check  if it is a negative-weighted edgeif (Graph->G[s][t]<0)   return false;// check if dist[t] needs updateif (dist[t]>dist[s]+Graph->G[s][t])dist[t]=dist[s]+Graph->G[s][t];}}}return true;
}

多源最短路径

  按照上面已有的Djikstra算法,可以直接将Djikstra算法在每个顶点调用一遍,然后找最小值。时间复杂度为:

直接找dist最小:

最小堆维护dist:

  另外还有一种更为直接的算法是Floyd算法,且代码看着简洁直观优美。。。

bool Floyd(){int i, j, k;for (k=0;k<Graph->Nv;k++)for (i=0;i<Graph->Nv;i++)for (j=0;j<Graph->Nv;j++)if (dist[i][k]+dist[k][j]<dist[i][j]){dist[i][j]=dist[i][k]+dist[k][j];if (i==j && dist[i][j]<0) //发现负值回路return false;}return true;
}

  三重循环嵌套,可以直观看出时间复杂度为

。意思也很明白,对于每对源点i和终点j,找一个跳板k,看是否经过跳板k距离会比现有的从i到j的距离短,如果是,则更新;否则不作为。发现负值回路的话解不存在,错误返回。

参考资料:

  1. coursera的《算法分析与设计》(以前的课程)
  2. MOOC网的《数据结构与算法》

小结

  距离上次更新已经有很长的时间,一方面是因为没整理好,包括现在记录的也觉得很混乱,就这么先把笔记记下来吧,不然时间久了更迷糊。另一方面是身体原因,年纪轻轻竟然椎间盘膨出了,不知道知乎上有没有啥治疗良方,反正去了好几次医院都说没事,直到痛得不行让拍CT了才知道是腰椎间盘膨出☹☹☹

   扯远了❦❦❦,剩下还有一些最小生成树,和拓扑排序啥的等我更明白点再写吧~~~

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

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

相关文章

vue中用数组语法绑定class

简单的绑定class就不说了&#xff0c;它可以和对象语法一样&#xff0c;使用data、computed、methods三种方法。说一下我在工作里体会到这种作法的好处。那么直接上代码。。。咔咔咔 说下需求&#xff0c;我是做一个显示框&#xff0c;当status为1时&#xff0c;代表成功状态&a…

解释型语言与编译型语言的区别

编译型语言在程序执行之前&#xff0c;有一个单独的编译过程&#xff0c;将程序翻译成机器语言&#xff0c;以后执行这个程序的时候&#xff0c;就不用再进行翻译了。 解释型语言&#xff0c;是在运行的时候将程序翻译成机器语言&#xff0c;所以运行速度相对于编译型语言要慢。…

三星台式机计算机编号怎么看,三星笔记本如何查看型号

现如今&#xff0c;电脑的用途广泛&#xff0c;而且方便快捷&#xff0c;深受人们的欢迎&#xff0c;人们不仅可以通过电脑来了解知识&#xff0c;开阔眼界&#xff0c;而且电脑是一种消遣、娱乐的方式&#xff0c;可以放松身心。那电脑的话&#xff0c;有分两种&#xff0c;一…

自旋锁和互斥锁实例_多线程编程之自旋锁

一、什么是自旋锁一直以为自旋锁也是用于多线程互斥的一种锁&#xff0c;原来不是&#xff01;自旋锁是专为防止多处理器并发(实现保护共享资源)而引入的一种锁机制。自旋锁与互斥锁比较类似&#xff0c;它们都是为了解决对某项资源的互斥使用。无论是互斥锁&#xff0c;还是自…

如何卸载symantec

前段时间,业务的虚机上安装了symantec Endpoint Protection(正版)&#xff0c; 发现虚机运行一段时间就会失去响应死机&#xff0c;并且有些安装symantec的虚机3389端口无法使用&#xff0c;怎么折腾都不行。最后决定卸载它。一、是否可以用停止服务和终止进程再卸载的方式卸载…

CSS文件引入顺序

<link rel"stylesheet" href"bootstrap.min.css"> <link rel"stylesheet" href"app.css"> 自定义的css要最后引入。因为有时候会修改bootstrap的css。只有后引入的才会覆盖。 如果提前引入了&#xff0c;自定义的会被bo…

浏览器的简单兼容

2019独角兽企业重金招聘Python工程师标准>>> function getXHER() { var xhr null; if(XMLHttpRequest){ xhr new XMLHttpRequest(); }else{ xhr new ActiveXObject(Microsoft.XMLHTTP); } return xhr; }转载于:https://my.oschina.net/u/2511906/blog/1865622

用计算机算出陈赫手机号码,陈赫手机号码遭《快本》曝光,并被网友打到关机!还有人搜到了他的支付宝账户......

原标题&#xff1a;陈赫手机号码遭《快本》曝光&#xff0c;并被网友打到关机&#xff01;还有人搜到了他的支付宝账户...昨天的陈赫可能是被不断的电话铃声叫醒的&#xff0c;因为快本在节目中把陈赫的电话号码给曝光了……当时导演让每个明星向自己的一位圈内好友发出求助短信…

伯努利分布方差_统计分布--深入浅出统计学总结

伯努利分布&#xff1a;一个实验只有两个结果概率发生在{0,1}&#xff0c;发生一个事件成功的概率为 x&#xff0c;不成功的概率为y&#xff0c; 1.若符合伯努基分布条件&#xff1a;p 成功概率 &#xff0c; q 失败概率伯努利分布数学期待&#xff1a;伯努利分布方差&#x…

解决The project description file (.project) for 'xxx' is missing

若没有修改过.project文件&#xff0c;只需重新导入工程即可&#xff0c;别纠结其他的了

macbook禁用键盘_一行命令禁用 MacBook 内置键盘

去年底阿麦换了新的 MacBook Pro&#xff0c;于是她自学生时代就一直在用的老款 MacBook Pro 就归我当玩具了。一度考虑过将其出售&#xff0c;但是想到自己还闲置了一块 SSD&#xff0c;就想着干脆换上让它继续服役。于是买了光驱硬盘支架&#xff0c;想着有时间就给换上。然而…

Java分享笔记:自定义枚举类 使用enum关键字定义枚举类

在JDK1.5之前没有enum关键字&#xff0c;如果想使用枚举类&#xff0c;程序员需要根据Java语言的规则自行设计。从JDK1.5开始&#xff0c;Java语言添加了enum关键字&#xff0c;可以通过该关键字方便地定义枚举类。这种枚举类有自己的程序编写规则&#xff0c;并且具有一些特殊…

html5做咖啡网页素材,HTML5/CSS3咖啡品类切换动画

CSS语言&#xff1a;CSSSCSS确定body {background-color: #FB9F89;}.container {position: absolute;top: 30px;left: 200px;}.saucer {position: absolute;top: 50px;left: 40px;width: 200px;height: 200px;border-radius: 100%;background-color: #FFF;box-shadow: 5px 1px …

汽车和山羊问题matlab仿真_Matlab----无人机集群对抗中的关键问题和仿真平台(开发中)案例...

无人机集群对抗&#xff0c;是自动驾驶中路径规划的新问题&#xff0c;并且连续两年出现在最近的中国大学生数学建模竞赛中。可见&#xff0c;这是一个急需解决的数学问题&#xff08;体现了官方的军事战略意志&#xff09;&#xff0c;同时&#xff0c;还没有成熟解决方案的问…

使用durid的ConfigFilter对数据库密码加密

原文连接&#xff1a;http://blog.csdn.net/aixiaoyang168/article/details/49930513 ----------------------------------------------------------------------- 对于大部分程序员来说&#xff0c;数据库的信息&#xff0c;如用户名&#xff0c;密码等信息一般都写到配置文件…

序(不知道是什么时候的模拟题)

序 【问题背景】 zhx 给他的妹子们排序。 【问题描述】 \(zhx\) 有 \(N\) 个妹子&#xff0c; 他对第 \(i\) 个妹子的好感度为\(a_i\), 且所有\(a_i\),两两不相等。 现在 \(N\) 个妹子随意站成一排&#xff0c; 他要将她们根据好感度从小到大排序。 他使用的是冒泡排序算法&…

html写用户导入,用户基本信息录入.html

&#xfeff;用户基本信息录入$axure.utils.getTransparentGifPath function() { return resources/images/transparent.gif; };$axure.utils.getOtherPath function() { return resources/Other.html; };$axure.utils.getReloadPath function() { return resources/reload.…

adg oracle 架构_技术栈数据中心有了ADG架构就高枕无忧了?你还需要做这一步!...

技术栈数据中心有了ADG架构&#xff0c;就高枕无忧了&#xff1f;你还需要做这一步&#xff01;如果把数据中心建设比喻成西天取经&#xff0c;那旅途上的九九八十一难就是我们不得不躲闪、跨越、攻坚的堡垒。即日起&#xff0c;希嘉推出“技术栈”板块&#xff0c;集结数据治理…

String length must be a multiple of four.

今天在整理2013年的工作时的一个项目&#xff0c;修改了数据库连接&#xff0c;初始化数据库&#xff0c;部署运行报错&#xff0c;主要原因是阿里巴巴druid报错&#xff0c;导致DataSource初始化失败。 druid报错日志&#xff1a; Caused by: java.lang.IllegalArgumentExce…

论文笔记:Person Re-identification with Deep Similarity-Guided Graph Neural Network

Person Re-identification with Deep Similarity-Guided Graph Neural Network 2018-07-27 17:41:45 Paper&#xff1a; https://128.84.21.199/pdf/1807.09975.pdf 本文将 Graph Neural Network (GNN) 应用到 person re-ID 的任务中&#xff0c;用于 model 不同 prob-gallery …