9.1 若干定义

  • 图的定义:一个图(Graph) G=(V,E)是由顶点的集合V和边Edge的集合E组成的。每一条边就是一个顶点对(v,w),其中(v,w) ∈E。有时候也把边叫做弧。如果顶点对是有序的,那么图就是有向的。有的图也叫做有向图。顶点w和顶点v邻接当且仅当(v,w)∈E。在一个具有边(v,w)从而具有边(w,v)的无向图中,w和v邻接且v和w邻接。有时候边还有第三种成分,成为权值。

  • 图的路径:图中的一条路径是指一个顶点序列w1,w2,w3,w4…,wn使得(wi,wi+1)∈E,这样一条路径的长就是改路径的边的数量,它等于N-1。从一个顶点到它自身可以看做一个路径,如果路径不包含边,则该路径的长为0。一条简单路径就是这样一条路径,其上的所有顶点都是互异的,但第一个顶点和最后一个顶点可能相同

  • 环:如果图含有一个顶点到它自身的边(v,v),那么路径v,v叫做环。我们要讨论的图一般都是无环的。

  • 有向图的圈:满足w1=wn,且至少长为1的一条路径;如果该路径是简单路径,那么这个圈就是简单圈。对于无向图来说,我们要求边是互异的。

  • DAG有向无环图:如果一个有向图没有圈,那么这个有向图就是有向无环图。


  • 连通性:如果一个无向图中从每个顶点到其他顶点都存在一个路径,那么这个无向图就是连通的。

  • 强连通性:如果一个有向图中从每个顶点到其他顶点都存在一个路径,那么这个无向图就是强连通的。

  • 若连通性:如果一个有向图不是强连通的,但是它的基础图(即其弧上去掉方向所形成的的图,)是连通的。那么该有向图就是弱连通图。

  • 完全图:图中每一对顶点之间都存在一条边的图。


  • 图的表示

若图是稠密的图,我们就可以采用邻接矩阵(使用一个二维数组)来表示这个图。对于每条边(v,w),置A[u][v]等于true,否则这个数组的元素就是false。如果变有一个权值,那么就可以置这个权值为A[u][v]的值,而使用一个很大的值和一根小的值来表示不存在的边。

如图示稀疏的图,我们就可以采用邻接表来表示这个图。对于每一个顶点,我们使用一个表来存放这个顶点所有邻接的顶点。此时的空间需求为O(|E|+|V|)。

对于有几种方式保留邻接表。首先注意到的是,这些邻接表本身可以被保存在任何类的List中,即ArrayList和LinkedList中。然而,对于非常稀疏的图来说,当使用ArrayList的时候,程序员可能需要从一个比默认更小的容量开始ArrayList;否则可能会造成空间的浪费

还有一种方式就是使用一个映射,在这个映射下,关键字就是那些顶点,而他们的值就是那些邻接表,或者把一个邻接表作为Vetex类的数据成员保存下来。

9.2 拓扑排序

拓扑排序是对有向无圈图的顶点的一种排序,使得如果存在一条从vi到vj的路径,那么排序中vi就出现在vj的后面。

一个简单的拓扑排序的算法就是先找出任意一个没有入边的顶点。然后显示出该顶点,并将它从其边中删除。然后我们图的其他部分同样应用这样的方法。

  • 入度:顶点v的入度定义为(u,v)的条数
  • 出度:顶点u的出度定义为(u,v)的条数。

9.3 最短路径算法


  • 单源最短路径

给定一个赋权图G = (V,E) 和一个特定顶点s作为输入,找出从s 到G中每一个其他顶点的最短赋权路径。

9.3.1 无权最短路径

广度优先遍历:该方法按层处理顶点:距开始最近的那些顶点首先被求值,而最远的那些顶点最后被求值。这很像树的层次遍历。

使用对拓扑排序的同样的分析,我们看到,只要是使用邻接表,则运行时间就是O(|E|+|v|)。

9.3.2 Dijkstra算法

如果是赋权图,那么问题无疑就变得非常困难了,不过我们仍然可以使用来自无权情形时的想法。

解决单源最短路径的一般方法叫做Dijkstra算法。Dijkstra算法按照阶段进行,正像无权最短路径算法一样。在每个阶段,Dijkstra算法选择一个顶点v,他在所有unknown顶点中具有最小的dv,同时算法声明从s到v的最短路径是known的。阶段的其余部分由dw的更新工作组成。

下面我们给出Dijkstra算法伪代码

Dijkstra算法 实现

public class Dijkstra {//创建地图
/** S——16——>C—— 2——>D* | \     ^ ^     ^* 4  8    |  \    |* |    \  7   5   6* v     v |    \  |* A—— 3——>B—— 1——>E*/public static class Graph {Map<Character,List<Node>> map=new HashMap<Character,List<Node>>();//输出的地图public Graph() {List<Node> list=new ArrayList<Node>();list.add(new Node('S','A',4));list.add(new Node('S','B',8));list.add(new Node('S','C',16));list.add(new Node('A','B',3));list.add(new Node('B','C',7));list.add(new Node('B','E',1));list.add(new Node('C','D',2));list.add(new Node('E','C',5));list.add(new Node('E','D',6));for(int i=0;i<list.size();i++) {List<Node> temp = map.get(list.get(i).getSrc());if(temp==null)temp=new ArrayList<Node>();temp.add(list.get(i));map.put(list.get(i).getSrc(), temp);}}
}public static class Node {private Character src;//起点private Character des;//终点private int len;      //距离长度private int path=Integer.MAX_VALUE;//初始设置为无穷长boolean known=false;    //访问过一次后就死亡public Node(){}public Node(Character src,Character des,int len) {this.src=src;this.des=des;this.len=len;}void setPath(int path) {this.path=path;}int getPath() {return path;}Character getSrc() {return src;}Character getDes() {return des;}int getLen() {return len;}
}public static Map<Character,Integer> dijkstra(Map<Character,List<Node>> map,Character c) {Queue<Node> heap=new PriorityQueue<>();//初始节点Node root=new Node(c,c,0);root.setPath(0);heap.add(root);Map<Character,Integer> result=new HashMap<Character,Integer>();while(!heap.isEmpty()) {Node x=heap.poll(),y = null;List<Node> temp=map.get(x.getDes());if(temp==null)continue;for(int i=0;i<temp.size();i++) {y=temp.get(i);if(y.getPath() > x.getDes()+y.getLen())temp.get(i).setPath(x.getPath()+y.getLen());if(!temp.get(i).known) {heap.add(temp.get(i));temp.get(i).known=true;}if(result.get(temp.get(i).getDes())==null) {result.put(temp.get(i).getDes(),temp.get(i).getPath());}if(result.get(temp.get(i).getDes())>temp.get(i).getPath()) {result.put(temp.get(i).getDes(),temp.get(i).getPath());}}}return result;}public static void main(String[] argc){Dijkstra.Graph graph=new Dijkstra.Graph();Map<Character,Integer> result=dijkstra(graph.map,'S');for(Map.Entry<Character,Integer> entry:result.entrySet()){System.out.println("S-->"+entry.getKey()+" 长度"+entry.getValue());}}
}

9.3.3 具有负边值的图

如果图负的边值,那么Dijkstra算法是行不通的。问题在于,一旦一个顶点被声明是known的,那么就可能从某个另外的unknown顶点v有一条回到u的负值的路径。

所以我们的解决方案就是,我们要忘记了关于unknown的顶点的概念,因为我们的算法要能够改变它的意向,下面是具有负值的赋值最短路径的伪代码:

9.3.4 无圈图

如果我们知道图是无圈的,那么我们可以通过改变声明顶点known的顺序,或者叫作顶点选取法则,来改进Dijkstra算法。新法则是以拓扑顺序选择顶点。由于选择和更新可以在拓扑排序执行的时候进行,因此算法可以一趟完成。

因为当一个顶点被选取以后,按照拓扑排序的法则它没有从known顶点发出的进入边,因此它的距离dv可以不再被降低,所以这种选取法则是行的通的。

无圈图的一个更重要的应用就是关键路径法。我们用如下动作节点图作为一个例子。每个节点表示一个必须执行的动作以及完成动作所要花费的时间。因此,该图叫做动作节点图。

9.4 最小生成树问题

最小生成树问题:一个无向图的最小生成树就是由该图的那些连接G的所有顶点的边构成的树,且总价值是最小的。

上面图中最小生成树种的边的条数为(|V|-1)

9.4.1 Prim算法

算法思想:在算法的任意时刻,我们都可以看到一组已经添加到树上的顶点,而其余顶点尚未添加到这个树中。此时,算法在每一个阶段都可以通过(u,v)使得(u,v)中的值是所以u在树上但v不在树上的边的值中的最小者而找出一个新的顶点并把它添加到这可树中。

这个算法的实现和Dijkstra算法是一样的,对于Dijkstra算法分析所做的一件事都可以用到这里。

9.4.2 Kruskal算法

Kruskal算法的贪婪策略就是联系按照最小的权值选择边,并且当所选的边不产生圈的时候就把他作为所选取的边。

形式上,Kruskal算法就是在处理一个深林----树的集合。开始的时候,存在|V|颗单节点的树,而添加一边就可以将两颗树合并成一颗树。当算法终止的时候,就只有一颗树了,这棵树就是最小生成树。

9.5 深度优先搜索的应用

深度优先遍历是对先序遍历的推广。我们从某个顶点u开始出来v,然后递归地开始遍历所有与v邻居的顶点。我们对任意的图进行该过程,那么我们要小心仔细的避免圈的出现。为此,当访问一个顶点v的时候,由于我们当时已经到了该点处,因此可以标记该点是访问过的,并且对于尚未被标记的所有邻居顶点来说递归调用深度优先遍历。

9.5.1 无向图

深度优先搜索的伪代码:

数据结构和算法:第八章 图论算法相关推荐

  1. 大数据算法系列12:图论算法

    文章目录 一. 图的概念 1.1 图的举例说明 1.2 图的常用概念 1.3 图的表示方式 1.3.1 邻接矩阵 1.3.2 邻接表 二. 图基本算法 参考: 一. 图的概念 图(Graph),是一种 ...

  2. 为实习准备的数据结构(11)-- 图论算法 集锦

    文章目录 讲个故事 图的相关定义 定义一:有向图.无向图.权重.活用图 定义二:完全图.连通图.连通分量.生成树 定义三:邻接表.邻接矩阵 定义四:DFS.BFS 定义五:Prim 算法.Kruska ...

  3. warshall算法求传递闭包c++_【建模小课堂】图论算法

    图论算法 图论算法在计算机科学中扮演着很重要的角色,它提供了对很多问题都有效的一种简单而系统的建模方式.很多问题都可以转化为图论问题,然后用图论的基本算法加以解决.这类问题算法主要包括Dijkstra ...

  4. 图论算法 有图有代码 万字总结 向前辈致敬

    图的定义 背景知识 看到这篇博客相信一开始映入读者眼帘的就是下面这幅图了,这就是传说中的七桥问题(哥尼斯堡桥问题).在哥尼斯堡,普雷格尔河环绕着奈佛夫岛(图中的A岛).这条河将陆地分成了下面4个区域, ...

  5. 【liuyubobobo-玩转图论算法】第一章 课程概述

    持续学习&持续更新中- 守破离 [liuyubobobo-玩转图论算法]第一章 课程概述 图论概述 课程特色 课程大纲 图论的应用 参考 <玩转数据结构>是<图论课程> ...

  6. 图论算法—图的拓扑排序介绍和Kahn算法原理解析以及Java代码的实现

    详细介绍了图的拓扑排序的概念,然后介绍了求拓扑序列的算法:Kahn算法的原理,最后提供了基于邻接矩阵和邻接表的图对该算法的Java实现. 阅读本文需要一定的图的基础,如果对于图不是太明白的可以看看这篇 ...

  7. 图论 物联网_图论算法-和图论算法相关的内容-阿里云开发者社区

    数学建模需掌握的知识总纲 数学建模需要掌握许多知识,这里我列出总纲: 学建模中的算法 穷举法 神经网络 模拟退火 遗传算法 图论算法 蒙特卡洛算法 所需基础知识 高等数学 线性代数(矩阵加减乘除) 概 ...

  8. 排序中减治法算法伪代码_【算法与数据结构】伪代码与流程图

    js难的是抽象概念 编程语言也能抽象 JS Python java PHP 根本就没区别 这些语言都有 声名 if...else while 循环 for 循环 函数 对象 这意味着 你没有必要过分关 ...

  9. NOIp 图论算法专题总结 (1):最短路、最小生成树、最近公共祖先

    系列索引: NOIp 图论算法专题总结 (1) NOIp 图论算法专题总结 (2) NOIp 图论算法专题总结 (3) 最短路 Floyd 基本思路:枚举所有点与点的中点,如果从中点走最短,更新两点间 ...

最新文章

  1. 单链表逆序生成及逆置的完整实现
  2. ubuntu install fonts
  3. find -exec 与xargs 区别
  4. WSS 3.0 和 sharepoint 2007 中文SDK
  5. c语言五子棋评估函数,简易五子棋评估函数
  6. LG已停止手机生产 手机生产线向家电制造转变
  7. 全世界到底有多少软件开发人员?
  8. android取消内存限制吗,Android 内存限制
  9. vsftp 客户端多个ip_VSFTP环境搭建
  10. 多校3 1008 Solve this interesting problem
  11. C++文件读写 ifstream ofstream 完成复制文件功能
  12. 001.XE3添加TPerlRegEx
  13. Java GC种类以及触发时机
  14. Unix网络编程5种IO模型
  15. C语言将txt文本文档数据重新排序并放至新txt文本文档中
  16. devops实践指南_最终的DevOps招聘指南
  17. 遗传算法pid matlab程序解释,关于遗传算法的pid整定问题
  18. 周三送书 | 白帽子讲WEB安全
  19. 三层架构,网关冗余(网关在汇聚层的情况下)
  20. 【ML】MoG与EM:从EM到MoG

热门文章

  1. PullToRefreshListView下拉刷新与上拉载入
  2. python 爬虫 记录
  3. 通过声明Attribute属性改变不同类的输出效果
  4. hdu 3629 Convex
  5. Struts编程心得
  6. 索引存储和散列存储(哈希)的区别吧
  7. 沃尔沃投资两家以色列科技创企 布局人工智能
  8. 前谷歌工程师:如何看待程序员普遍缺乏数据结构和算法知识?
  9. DFT,DTFT,DFS,FFT之间的关系以及序列补零和插值对频域的影响
  10. mysql聚合函数不存在的数据_SELECT子句中不存在聚合函数时的GROUP BY行为