a - 数据结构实验之图论一:基于邻接矩阵的广度优先搜索遍历_数据结构--图

故事凌 今天

基本知识点

图可说是所有数据结构里面知识点最丰富的一个, 自己笨的知识点如下:

  • 阶(oRDER), 度: 出度(out-Degree), 入度(in-Degree)
  • 树(Tree), 森林(Forest), 环(Loop)
  • 有向图(Directed Graph), 无向图(Undirected Graph), 完全有向图, 完全无向图
  • 连通图(Connected Graph), 连通分量(Connnected Component)
  • 存储和表达式: 邻接矩阵(Adjacency Matrix), 邻接链表(Adjacency List)

围绕图的算法也是五花八门

  • 图的遍历: 深度优先, 广度优先
  • 环的检测: 有向图, 无向图
  • 拓扑排序
  • 最短路径算法: Dijkstra, Bellman-Ford, Floyd Warshall
  • 连通性相关算法: Kosaraju, Tarjan, 求解孤岛的数量, 判断是否为树
  • 图的着色, 旅行商问题等

以上的知识点知识图轮里的冰山一角, 对于算法面试而言, 完全不需要对每个知识点都一一掌握, 而应该有的放矢的准备

必会的知识点

以下的知识点必须充分掌握并反复练习

  • 图的存储和表达式: 邻接矩阵(Adjacency Matrix), 邻接链表(Adjacency List)
  • 图的遍历: 深度优先, 广度优先
  • 二部图的检测(Bipartite), 数的检测, 环的检测, 有向图, 无向图
  • 拓扑排序
  • 联合-查找算法(Union-Find)
  • 最短路径Dijkstra, BellMan-Ford

其中, 环的检测, 二部图的检测, 树的检测以及拓扑都是基于图的遍历, 尤其是深度优先方式的遍历, 而遍历可以在邻接矩阵或者邻接链表上进行, 所以掌握好图的遍历是重中之重, 因为它是所有其他图论算法的基础

至于对端路径算法, 能区分它们的不同特点, 知道在什么情况下用哪种算法就很好了, 对于有充足时间准备的面试者, 能熟练掌握他们的写法当然是很好的

我们来来看看数据结构中的图到底是什么

1. 图的定义

图是由一些点(vertex)和这些点之间的连线(edge)所组成的, 其中, 点通常称为顶点(vertex), 而点到点之间的连线通常称之为边或者弧(edge), 通常记为G = (V,E)

2. 图的分类

图通常分为有向图和无向图, 而其表示方式分为邻接矩阵和邻接链表, 具体表示如下图.

e954dbc164acb28ce434c89c0517d996.png

对于无向图,其所有的边都不区分方向。G=(V,E)。其中,

  1. V={1,2,3,4,5}。V表示有”1,2,3,4,5”几个顶点组成的集合。
  1. E={(1,2),(1,5),(2,1),(2,5),(2,4),(2,3),(3,2),(3,4),(4,3),(4,2),(4,5),(5,1),(5,2),(5,4)}。E就是表示所有边组成的集合,如(1,2)表示由顶点1到顶点2连接成的边。
505c9db4c7132697545d153b240bea6d.png

对于有向图,其所有的边都是有方向的。G=(V,E)。其中,

  1. V={1,2,3,4,5}。V表示有”1,2,3,4,5”几个顶点组成的集合。
  2. E={<1,2>,<2,5><5,4>,<4,2><3,5>,<3,6>,<6,6>}。E就是表示所有边组成的集合,如<1,2>表示由顶点1到顶点2连接成的边。注意有向图边和无向图边表示方法的不同,有向图的边是矢量,而无向图只是普通的括号。

针对邻接矩阵和邻接链表两种不同的表示方式,有如下优缺点:

  1. 邻接矩阵由于没有相连的边也占据空间,相对于邻接链表,存在空间浪费的问题;
  1. 但是在查找的时候,邻接链表会比较耗时,对于邻接矩阵来说,它的查找复杂度就是O(1)。

用邻接表表示图的代码

#define MAX 100typedef struct ENode   //邻接表中表对应的链表的顶点{   int ivex;          //该边所指向的顶点的位置   int weight;       //该边的权值   struct ENode *next_edge;   //指向下一条边的指针}ENode,*PENode;typedef struct VNode   //邻接表中表的顶点{   char data;       //顶点的数据   struct VNode *first_edge;  //指向第一条依附该顶点的边}VNode;typedef struct LGraph  //邻接表{   int vexnum;    //图的顶点数   int edgenum;   //图的边数   VNode vexs[MAX];}LGraph;

3. 度, 权, 连通图等概念

对于无向图来说,它的顶点的就是指关联于该顶点的边的数目;而对于有向图来说,分为入度和出度,所谓入度就是进入该顶点边的数目,出度就是离开这个顶点边的数目,有向图的度就是入度加出度。

图还被分为有权图和无权图,所谓有权图就是每条边都具有一定的权重,通常就是边上的那个数字;而无权图就是每条边没有权重,也可以理解为权重为。如下图所示即为有权图,(A,B)的权就是13。

1fcace84c8c15c8a9d21cbeb30f5d2b8.png

如果一个无向图中每个顶点到所有其他顶点都是可达的,则称该图是连通的;如果一个有向图中任意两个顶点互相可达,则该有向图是强连通的。

如图(b)中有3个连通分量:{1,2,5},{3,6},{4}。若一个无向图只有一个连通分量,则该无向图连通。

而图(a)中有3个强连通分量:{1,2,4,5},{3},{6}。{1,2,4,5}中所有顶点对互相可达。而顶点2与6不能相互可达,所以不能构成一个强连通分量。

4. 深度优先搜索(Depth First Search DFS)

图的深度优先算法有点类似于树的前序遍历,首先图中的顶点均未被访问,确定某一顶点,从该顶点出发,依次访问未被访问的邻接点,直到某个邻接点没有未被访问邻接点时,则回溯父节点(此处我们将先被访问的节点当做后被访问节点的父节点,例如对于节点A、B,访问顺序是A ->B,则称A为B的父节点),找到父节点未被访问的子节点;如此类推,直到所有的顶点都被访问到。

注意,深度优先的存储方式一般是以栈的方式存储。

  1. 无向图的深度优先搜索
249f80281286eadfaa8cbc168532e1bc.png
  1. 有向图的深度优先搜索
1d0d055c698a64c3cb1b09262e670d2e.png
  1. 深度优先搜索代码
static void DFS(LGraph G, int i,int *visited){   Enode *node;E   printf(“%c”,G.vexs[i].data);   node = G.vexs[i].first_edge;   while(node != NULL)  {       if(!visited[node->ivex])           DFS(G, node->ivex, visited);  //递归调用DFS       node = node->next_edge;  }}

5. 广度优先搜索

从图中的某个顶点出发,访问它所有的未被访问邻接点,然后分别从这些邻接点出发访问它的邻接点。说白了就是一层一层的访问,“浅尝辄止”!

注意,广度优先搜索的存储方式一般是以队列的方式存储。

  1. 无向图的广度优先搜索
bd02cb7f23e2d5b6e34ac96745043b93.png
  1. 有向图的广度优先搜索
ccc75bd5d4052753d154e382f2766e7a.png
  1. 广度优先所有代码
void BFS(LGraph G){   int head = 0;   int rear = 0;   int queue[MAX];   //辅助队列   int visited[MAX];   //顶点访问标记   eNode *node;   for(int i = 0; i ivex;               if(!visited[k])              {                   visited[k] = 1;                   printf(“%c”,G.vesx[k].data);                   queue[rear++] = k;              }               node = node->next_edge;          }      }  }   printf(“”);}

6.拓扑排序

拓扑排序(Topological Order)是指讲一个有向无环图(Directed Acyclic Graph,DAG)进行排序而得到一个有序的线性序列。

举个例子,例如我们早上起床的穿衣顺序,如下图所示。穿衣的顺序也是有个优先级的,有些衣服就必须优先穿上,例如领带依赖于衬衣,所以领带最终排在衬衣之后;对图a中的元素进行合理的排序,就得到了图b的次序图。注意,该次序图不是唯一的。

9460c8d354fbaf339b1f10882896aa71.png
176c7f4a3505e2844d7fb905e91eb746.png
int topological_sort(LGraph G){   int num = G.vexnum;   ENode *node;   int head = 0;     //辅助队列的头   int rear = 0;     // 辅助队列的尾   int *ins = (int *)malloc(num * sizeof(int));       //入度数组   char *tops = (char *)malloc(num * sizeof(char));  //拓扑排序结果数组,记录每个节点排序后的序号   int *queue = (int *)malloc(num * sizeof(int));     //辅助队列   assert(ins != NULL && tops != NULL && queue != NULL)   memset(ins, 0, num * sizeof(int));   memset(tops, 0, num * sizeof(char));   memset(queue, 0, num * sizeof(int));   for(int i = 0; i ivex]++;           node = node->next_edge;      }  }   for(int i = 0; i ivex]--;    //将节点node关联的节点的入度减1           if(ins[node->ivex] == 0)   //若节点的入度为0,则将其添加到队列中               queue[rear++] = node->ivex;           node = node->next_edge;      }  }   if(index != G.vexnum)  {       printf(“Graph has a cycle!”);       free(queue);       free(ins);       free(tops);       return 1;  //1表示失败,该有向图是有环的  }   printf(“== TopSort: ”);   //打印拓扑排序结果   for(int i = 0; i 

7. 最小生成树

所谓最小生成树就是将图中的顶点全部连接起来,此时这个边的权重最小,并且连接起来的是一个无环的树。很容易知道,若此时的顶点是n,则边的数量为n-1。所以在一个图中找最小生成树就是找最小权值的边,让这些边连成一棵树。常用的算法有Prim算法和Kruskal算法。

7. 1Prim算法

该算法就是每次迭代选择权值最小的边对应的点,加入到最小生成树中。具体实现如下所示。

第一步:选取顶点A,此时U={A},V-U={B,C,D,E,F,G}。

1a1af0ebae41879cc30e706407b9a911.png

第二步:选取与A点连接的权值最小的边,此时就会选择到B,U={A,B},V-U={C,D,E,F,G}。

8b4bc707b0f5bdfc1071414018b3a979.png

以上面的步骤类推,得到如上图所示的结果,此时U={A,B,C,D,E,F},V-U={G}。注意到C是此次加入的点,而G没有加入,此时G点的边应该如何选择?

d19e7d8e03b8c7f40837f74e99873296.png

最终,得到如图所示的最小生成树,此时U={A,B,C,D,E,F,G},V-U={}。

94f707c4a4c210a0c9caac2e0ccbec2d.png
#define INF (~(0x1<<31))  //最大值(即0X7FFFFFFF)//返回ch在邻接表中的位置static int get_position(LGraph G, char ch){   for(int i = 0; i 的权值;若start到end不连通,则返回无穷大   int getWeight(LGraph G, int start, int end)  {       ENode *node;       if(start == end)           return 0;       node = G.vexs[start].first_edge;       while(node != NULL)      {           if(end == node->ivex)               return node->weight;       node = node->next_edge;      }       return INF;  }   void Prim(LGraph G,int start)  //从图中的第start个元素开始,生成最小树  {       int index = 0;    //prim最小树的索引,即prims数组的索引       char prims[MAX];  //prim最小树的结果数组       int wights[MAX];   //顶点间边的权重       //prim最小生成树中第一个数,即图中的第start个数       prims[index++] = G.vexs[start].data;       for(int i = 0; i 

7.2 Kruskal算法

该算法的核心就是对权值进行排序,然后从最小的权值开始,不断增大权值,如何该权值的所在边的两个顶点没有存在的路径连在一起,则加入这条边,否则,则舍弃这条边,知道所有的点都在这颗树中。

如下所示的一个图,我们从中找出最小生成树。

8464cea93d4baa01fe1e0b37869a660a.png
abf3cff35c0fd9909e9791cb6b373718.png

对于左边所示的图,对各个边的权值排序之后,我们最先找到权值最小的边,即AD。然后我们发现还有一个CE,于是CE也会被标记起来。

对于左边所示的图,对各个边的权值排序之后,我们最先找到权值最小的边,即AD。然后我们发现还有一个CE,于是CE也会被标记起来。

typedef struct edata  //边的结构体{   char start;  //边的起点   char end;   //边的终点   int weight;  //边的权重}EData;EData *get_edges(LGraph G){   int index = 0;   ENode *node;   EData *edges;   edges = (EData *)malloc(G.edgnum * sizeof(EData));   for(int i = 0; i ivex > i)          {               edges[index].start = G.vexs[i].data;               edges[index].end = G.vexs[node->ivex].data;               edges[index].weight = node->weight;               index++;          }           node = node->next_edge;      }  }   return edges;}void Kruskal(LGraph G){   int index = 0;     //rets数组的索引   int vends[MAX] = {0};    //用于保存“已有最小生成树”中每个顶点在该最小树中的终点   EData rets[MAX];    //结果数组,保存kruskal最小生成树的边   EData *edges;     //图对应的所有边   edges = get_edges(G);   //获取图中所有的边   Sorted_edges(edges, G.edgenum);   //对边按照权值进行排序   for(int i = 0; i 

例题分析

785. 判断二分图

给定一个无向图graph,当这个图为二分图时返回true。

如果我们能将一个图的节点集合分割成两个独立的子集A和B,并使图中的每一条边的两个节点一个来自A集合,一个来自B集合,我们就将这个图称为二分图。

graph将会以邻接表方式给出,graph[i]表示图中与节点i相连的所有节点。每个节点都是一个在0到graph.length-1之间的整数。这图中没有自环和平行边:graph[i] 中不存在i,并且graph[i]中没有重复的值。

示例 1:输入: [[1,3], [0,2], [1,3], [0,2]]输出: true解释:无向图如下:0----1|   ||   |3----2我们可以将节点分成两组: {0, 2} 和 {1, 3}。
示例 2:输入: [[1,2,3], [0,2], [0,1,3], [0,2]]输出: false解释:无向图如下:0----1|  ||  |3----2我们不能将节点分割成两个独立的子集。

好了, 自己研究了半天, 题都没有研究明白,我决定放弃了, 要是哪个大佬知道, 快来教教我吧, 我们今天把树是个什么东西就好了!

graph 的长度范围为 [1, 100]。graph[i] 中的元素的范围为 [0, graph.length - 1]。graph[i] 不会包含 i 或者有重复的值。图是无向的: 如果j 在 graph[i]里边, 那么 i 也会在 graph[j]里边。

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

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

相关文章

vim: vimrc

2019独角兽企业重金招聘Python工程师标准>>> 打造vim CIDE http://blog.csdn.net/doc_sgl/article/details/47205779 转载于:https://my.oschina.net/u/2528742/blog/843176

计算机二级考试试题在线看,【TOP182015年全国计算机二级考试试题题库.doc文档免费在线阅读材料】...

TOP182015年全国计算机二级考试试题题库.doc文档免费在线阅读《2015年全国计算机二级考试试题题库.doc》由会员分享&#xff0c;可免费在线阅读全文&#xff0c;更多与《TOP182015年全国计算机二级考试试题题库.doc文档免费在线阅读》相关文档资源请在帮帮文库(www.woc88.com)数…

不用开发实现RDS RDWeb门户美化和个性化

个性化RDWeb界面RDWeb原生界面相对比较简洁&#xff0c;每个企业部署的RDWeb都是千篇一律的&#xff0c;有些用户可能希望将网页装饰得个性化点。在谈到自定义Web界面&#xff0c;第一反应可能是使用代码进行编写&#xff0c;但是这里要和大家分享的是无代码美化和自定义RDWeb界…

其他大神的配置 nginx 配置参考

2019独角兽企业重金招聘Python工程师标准>>> user nginx nginx; worker_processes 2; #error_log logs/error.log; error_log logs/error.log notice; #error_log logs/error.log info; pid logs/nginx.pid; google_perftools_profiles /tmp/tcmalloc; worker_rlim…

小孩用计算机做作业怎么表达,计算机作业

满意答案尧Dreaman夕2014.09.25采纳率&#xff1a;40% 等级&#xff1a;10已帮助&#xff1a;1121人Windows中可以设置、控制计算机硬件配置和修改桌面布局的应用程序是( D)A) Word B) Excel C)资源管理器 D)控制面板多窗口的切换可以通过(D )来实现A)在任务栏上用鼠标单击右…

[No0000178]改善C#程序的建议1:非用ICloneable不可的理由

好吧&#xff0c;我承认&#xff0c;这是一个反标题&#xff0c;实际的情况是&#xff1a;我找不到一个非用ICloneable不可的理由。事实上&#xff0c;接口ICloneable还会带来误解&#xff0c;因为它只有一个Clone方法。 我们都知道&#xff0c;对象的拷贝分为&#xff1a;浅拷…

SOM 的两种算法

我参考了这篇文章http://www.scholarpedia.org/article/Kohonen_network另一个很好的演示在这里http://www.math.le.ac.uk/people/ag153/homepage/PCA_SOM/PCA_SOM.htmlSOMt是训练步一个输入数据是n维向量待训练的是一堆节点&#xff0c;这堆节点之间有边连着&#xff0c;通常是…

秒懂,Java 注解 (Annotation)你可以这样学

转自: https://blog.csdn.net/briblue/article/details/73824058 文章开头先引入一处图片。 这处图片引自老罗的博客。为了避免不必要的麻烦&#xff0c;首先声明我个人比较尊敬老罗的。至于为什么放这张图&#xff0c;自然是为本篇博文服务&#xff0c;接下来我自会说明。好了…

Java技术中的三大特性

为什么80%的码农都做不了架构师&#xff1f;>>> 1&#xff0e;虚拟机 Java虚拟机JVM&#xff08;Java Virtual Machine&#xff09;在Java编程里面具有非常重要的地位&#xff0c;约相当…

matlab图像增强分段线性函数_图像增强、锐化,利用 PythonOpenCV 来实现 4 种方法!...

图像增强目的使得模糊图片变得更加清晰、图片模糊的原因是因为像素灰度差值变化不大&#xff0c;图片各区域产生视觉效果似乎都是一样的&#xff0c; 没有较为突出的地方&#xff0c;看起来不清晰的感觉解决这个问题的最直接简单办法&#xff0c;放大像素灰度值差值、使图像中的…

计算机考研保护一志愿,考研良心大学,保护一志愿的考研名校!

大家好&#xff0c;我是&#xff0c;今天胖胖要跟大家送一些重要的干货&#xff0c;就是对于选学校的小伙伴来说也好&#xff0c;或者是即将要参加研究生复试的小伙伴们来好胖胖在这里要跟大家说一个关于考研白名单的事情&#xff0c;因为大家都知道考研是会分黑名单和白名单&a…

华为备份历史版本_华为手机NAS备份时提示“需处于同一局域网”的解决方法

本内容来源于什么值得买APP&#xff0c;观点仅代表作者本人 &#xff5c;作者&#xff1a;噩梦飘雷创作立场声明&#xff1a;在使用华为手机向群晖NAS中备份时发现一直无法成功&#xff0c;经过一番研究找到了解决方案&#xff0c;希望能帮到大家~前言最近看了一位老哥的帖子&a…

【原创】自己编写的JavaGUI一键生成(hibernate/spring/mvc/maven)工具(附带视频教程源码)...

为什么80%的码农都做不了架构师&#xff1f;>>> 带项目源码&#xff08;https://git.oschina.net/qsyan/GeneratorFx&#xff09; app下载地址(附带视频教程)&#xff1a;http://download.csdn.net/detail/juyan2008/9769406 注明&#xff1a;此应用采用javafx编写…

普通计算机怎么算根号_大学专业介绍 | 计算机专业的真实就业情况

前两天给大家简单介绍了近些年比较火的计算机类相关专业具体都有哪些不同&#xff0c;以及就业时的行业或者岗位的侧重点。今天呢我们继续这个话题&#xff0c;来聊一聊整个计算机相关专业在学习和就业过程中大概是什么样子的&#xff0c;希望能够给大家提供一些实实在在的参考…

设计模式总结篇系列:工厂方法模式(Factory Method)

工厂方法模式适合于对实现了同一接口或继承了同一父类的一些类进行实例的创建。一般是通过定义一个工厂类&#xff0c;并在其方法中实现对具有上述特点的类对象的创建。 根据具体产生类对象的方法定义形式&#xff0c;又可以将其分为普通工厂方法模式、多个工厂方法模式和静态工…

高新园区到大连计算机学校,大连高新区中心小学

大连市高新区中心小学简介&#xff1a;大连市高新区中心小学始建于2009年9月&#xff0c;是大连高新技术产业园区筹建的第一所直属公办学校。学校现拥有2000多名学生&#xff0c;87名教职员工。学校确定了“办诗韵教育&#xff0c;让每个孩子都幸福的教育理念”&#xff0c;通过…

0限流电阻 stm32_上/下拉电阻

除了前一节讨论的拉电阻基本使用方法外&#xff0c;上拉电阻也可以提升高电平的电压阈值&#xff0c;以便于前后级信号相匹配&#xff0c;比如&#xff0c;TTL逻辑电平驱动CMOS逻辑电平时&#xff0c;我们通常会添加一个上拉电阻R1&#xff0c;如下图所示&#xff1a;But Why&a…

网段和子网的区别_电焊石笼网与普通石笼网区别

对于石笼网&#xff0c;每个人都会想到在河中&#xff0c;桥梁附近和大坝上充满石头的笼子结构。今天&#xff0c;我们谈论的是石笼网的一种电焊石笼网&#xff0c;它也是一种常见的石笼网&#xff0c;也被称为六边形石笼网。其功能与石笼网相同&#xff0c;但用法与普通石笼网…

jquery $(document).ready() 与window.onload的区别

转载博客&#xff1a;http://www.jb51.net/article/21628.htm Jquery中$(document).ready()的作用类似于传统JavaScript中的window.onload方法&#xff0c;不过与window.onload方法还是有区别的。 1.执行时间 window.onload必须等到页面内包括图片的所有元素加载完毕后…

小学三年级上册计算机计划,小学三年级数学上册教学计划

小学三年级数学上册教学计划一、班级情况分析这一学期我继续担任三年级数学教学工作&#xff0c;本班现有学生9人&#xff0c;其中男生3人&#xff0c;女生6人。三年级学生已经有两年的数学学习经历&#xff0c;对一些基础性的数学知识有了初步的认识。学生已经比较习惯于新教材…