这一章又要学习一个新的体系了——图html
接下来的例子都是使用这两个图
java
一些共同的基础概念:git
自循环 :连通一个顶点及其自身的边称为自循环或环, 例如,边(A,A) 表示的是链接A到自身的一个环。算法
路径: 无向图中是指图中的一系列边,每条边连通两个顶点。例如,图1,ABD就是一条从A到D的路径。有向图中的路径是图中连通两个顶点的有向边序列。数组
无向图中的路径是双向的。例如,ABD确实是从A到D的路径,但因为这些边并无方向,所以反过来,DBA又是从D到A的路径。网络
路径的长度:是该路径中边的条数(或者是顶点数减去1)。如,从A到D的路径长度是2。注意, 这里对路径长度的定义等同于讨论树时用到的路径长度定义,由于, 树也是图的一种。数据结构
环路:是一种首顶点和末顶点相同且没有重边的路径。在图1中,咱们称路径ABCA是一条环路。没有环路的图称为无环的。函数
连通:在无向图中,若是其中的任意两个顶点之间都存在一条路径,则认为这个无向图是连通的(connected),连通有向图的定义听起来和连通无向图的定义同样。若是有向图中的任意两个顶点之间都存在一条路径, 则认为该有向图是连通的。可是记住,对有向图和无向图的路径定义是不一样的。
学习
无向图:测试
定义:无向图(undirected graph)是一种边为无序结点对的图。记做(A, B)的边就意味着A与B之间有一条从两个 方向均可以游历的链接 ,(A,B) 和记做(B,A) 的含义是彻底样的。
若是无向图拥有最大数目的连通顶点的边,则认为这个无向图是彻底的。也就是说,对于第一个顶点,须要(n一1)条边将其与其余顶点连通。对于第2个顶点,只需(n-2)条边(除去与第一个顶点相连的边)。第三个顶点须要 (n-3)条边。 因此,对有n个顶点的无向图,要使该图为彻底的,要求有n(n- 1)/2条边。注意,这里假设其中没有边是自循环的。
无向树(undirected tree)是一种连通的无环无向图,其中一个元素被指定为树根。
有向图
有向图(directed graph)有时也称为双向图(digraph),它是一种边为有序顶点对的图。这意味着边(A,B) 和边(B, A)在有向图中是不一样的有向边。
若是有向图中没有环路,且有一条从A到B的边,则能够把顶点A安排在顶点B以前。这种排列获得的顶点次序称为拓扑序(lopological order)
有向树是种指定了个元素做为树根的有向图,还有以下属性:
不存在其余顶点到树根的链接。
每一个非树根元素刚好有一个链接。
树根到每一个其余顶点都有一条路径。
网络
(这是一个网络,该网络描绘了在美国城市之间的连通性和飞机票价。这个加权图(或网络)能够用于肯定从个城市到另外一个城市的最便宜行程。)
在这里咱们学习的算法包括:各类遍历算法(这些遍历相似于探讨过的树遍历),寻找最短路径的算法,寻找网络中最低代价路径的算法,回答一些简单图相关问题(例如,图是不是连通的,图中两个顶点间的最短路径是什么,等等)的算法。
遍历
课本中用一个队列和一个无序列表来构造图的广度优先遍历。咱们将使用队列(traversalQueue)来管理遍历,使用无序列表(resultList) 来构造出结果。
用一个实例来讲明:
(1)往traversalQucue中添加9,而且把它标记为visited.
(2)从traversalQucuc中取出9.
(3)往resultList中添加9.
(4)往traversalQucue中添加六、7和8,网时把它们逐 标记为visited.
(5)从traversalQueuc中取出6。
(6)往resultList中添加6。
(7)往raversalQucue中添加三、4,同时把这两个顶点标记为vsited.
(8)从traversalQueue中取出7,并将它添加到resutList中。
(9)往traversalQueue中添加5,而且把它标记为visited.
(10)从traversalQueure中取出8,并将它添加到resutList中(这时再也不往traversal.Qucue中添加任何新的顶点,由于顶点8再也没有还没有访问过的邻居了).
(11)从traversalQueuc中取出3,并将它添加到resultList中。
(12)往traversalQueue中添加1,而且把它标记为visited.
(13)从traversalQueue中取出4,并将它添加到resultList中。
(14)往traversalQueue中添加2,而且把它标记为visited.
(15)从traversalQueue中取出5,并将它添加到resultList中(因为再也没有未访问过的邻居,所以不需再往traversalQueue中添加顶点)。
(16)从traversalQueue中取出1,并将它添加到resultList中(因为再也没有未访问过的邻居,所以不需再往traversalQueue中添加顶点)。
(17)从traversalQucue中取出2,并将它添加到resultList中。
这样,resultList 就以广度优先次序存放了从顶点9开始的以下顶点: 九、六、七、八、三、四、五、1和2。
图的深度优先遍历构造使用了一样的逻辑,只不过在深度优先遍历中用traversalStack代替了traversalQueue可是, 算法中还有一处不一样:在顶点还没有添加到resultList以前,咱们并不想标记该项点为visited.
测试连通性
最小生成树
生成树(spanningtree)是一棵含有图中全部顶点和部分边(但可能不是全部边)的树。
最小生成树(minimum spanningtree, MST)是加权图的最小生成树,它是一棵生成树, 其边的权重总和小于或等于同一个图中其余任何一棵生成树的权重总和。
算法:任意选取个起始顶点(无论是哪个), 并将它添加到最小生成树中。而后,将全部含该起始顶点的边按照权重次序添加到minheap (最小堆)中。(若是处理的是有向网络,则只会添加那些以这个特定顶点为起点的边。)接着从minheap中取出最小边,并将这条边和那个新顶点添加到最小生成树中。下一步,咱们往minbeap中添加全部含该新顶点的另外一顶点尚不在最小生成树中的边。继续这过程,直到最小生成树含有原始图中的全部顶点(或minheap为空)时结束。
断定最短路径
邻接列表:对图结点来讲,因为每一个结点能够有多达n-1条边与其余结点相连,所以最好用种相似于链表的动态结点来存储每一个结点带有的边。这种链表称为邻接列表(adjacency is)。对网络或加权图而言,每条边会存储成一个含权重的三元组。对无向图而言,边(A,B)会同时出如今顶点A和顶点B的邻接列表中。
邻接矩阵:邻接矩阵(adjacency matrix) 是一个二维数组。在邻接矩阵中,这个二维数组中的每一个单元都表示了图中两个顶点的交接状况。对矩阵中的任何单元(row, colum)而言,当且仅当图中存在(Vrow,Vcolum)时,这个单元才为true。因为无向图中的边是双向的,所以,若是(A, B)是图中的一条边,那么(B,A)一样也是图中的一条边。
、
(注意,这个矩阵是对称的,即该矩阵对角线的一侧是另外一 侧的镜像。 其缘由就在于它所表示的是个无向图。 对无向图来讲,没有必要表示整个矩阵,只需给出矩阵对角线的一侧(或另外一侧)便可。)
问题1:教材中讲述遍历的时候只详细介绍了广度优先遍历,和给出了深度优先遍历的代码,那深度优先遍历的状况是怎样的呢?并且遍历都是以无向图来讲明的,可是若是有向图又该是什么样的状况呢?
深度优先遍历的状况
有向图的遍历其实就是看图是不是连通的,看网上的这幅图
他的深度优先遍历状况就应该是:
第1步:访问A。
第2步:访问B。
在访问了A以后,接下来应该访问的是A的出边的另外一个顶点,即顶点B。
第3步:访问C。
在访问了B以后,接下来应该访问的是B的出边的另外一个顶点,即顶点C,E,F。在本文实现的图中,顶点ABCDEFG按照顺序存储,所以先访问C。
第4步:访问E。
接下来访问C的出边的另外一个顶点,即顶点E。
第5步:访问D。
接下来访问E的出边的另外一个顶点,即顶点B,D。顶点B已经被访问过,所以访问顶点D。
第6步:访问F。
接下应该回溯"访问A的出边的另外一个顶点F"。
第7步:访问G。
所以访问顺序是:A -> B -> C -> E -> D -> F -> G
他的广度优先遍历状况就应该是:
第1步:访问A。
第2步:访问B。
第3步:依次访问C,E,F。
在访问了B以后,接下来访问B的出边的另外一个顶点,即C,E,F。前面已经说过,在本文实现中,顶点ABCDEFG按照顺序存储的,所以会先访问C,再依次访问E,F。
第4步:依次访问D,G。
在访问完C,E,F以后,再依次访问它们的出边的另外一个顶点。仍是按照C,E,F的顺序访问,C的已经所有访问过了,那么就只剩下E,F;先访问E的邻接点D,再访问F的邻接点G。
所以访问顺序是:A -> B -> C -> E -> F -> D -> G
你们也能够參考一下这篇资料图的遍历之 深度优先搜索和广度优先搜索
问题1:编写pp15.1即用邻接列表实现无向图最难的在我看来是遍历的书写。
问题1解决方案:以前我找百度,看见里面的写法不少都是直接调用了Java类库中的ArrayList类,我也找同窗帮助看了看他们编写的思路,也有用了类库的那样他们的遍历方法能够继续使用原来的不用从新编写,可是我以为那个本质上使用的只是数组套数组,并无链表的影子。我还看到了另外一种写法,就是使用本身当初编写的指针类,像是这样的。
因此我想直接使用咱们当初编写的LinearNode类,这样就像咱们以前学习到的哈希函数的编写方法,可是这时的遍历方法就要改写。
对原来的代码进行了改写代码连接
问题2:
问题2解决方案:最近的的调试老是出现这样的问题,看样子是要解决一下了
(我以前还觉得是当初的那个代码写的有问题,如今才发现看样子是有哪里出问题了,否则之后都调试不了了)
其实个人编写思路是和馨雨同窗同样的,和她同样在写添加边的过程当中出现了越界的问题,我思考了好久都没有想出是为何(我以为本身的思路没有问题TAT),而后就去参考了一些同窗们的写法,最后屈服了,换了一种写法。
“由于堆是二叉搜索树,因此只有一个正确的位置用于插入新节点,而且若是级别h已满,该位置能够是从级别h左侧的下一个打开位置或在级别h+1左侧的第一个打开位置。”
这道题会错是由于本身只注意到了后面的讲述是对的,没看到最关键的前提 堆是一棵彻底树,才有后面的内容。
又是紧张的一周学习,其实这周的做业是上次做业都还没完成的状况下就发布了的,但仍是时间紧张呀。这周其实课本
上给出的代码不算多,因此代码做业就看起来挺多的。时光匆匆,貌似咱们这一本课本也学习完了(其实仍是没有吃透(つД`)),
立刻又要开始魔鬼般的实验做业了,但愿我不会给个人小伙伴们拖后腿了。
代码行数(新增/累积) | 博客量(新增/累积) | 学习时间(新增/累积) | ||
---|---|---|---|---|
目标 | 5000行 | 30篇 | 400小时 | |
第一周 | 0/0 | 1/1 | 10/10 | |
第二周 | 326/326 | 1/2 | 18/28 | |
第三周 | 784/1110 | 1/3 | 25/53 | |
第四周 | 2529/3638 | 2/5 | 37/90 | |
第五周 | 1254/4892 | 2/7 | 20/110 | |
第六周 | 1403/6295 | 2/9 | 32/142 | |
第七周 | 1361/7656 | 1/10 | 35/177 | |
第八周 | 2750/10406 | 2/12 | 32/209 | |
第九周 | 2444/12850 | 1/13 | 23/232 |
计划学习时间:25小时
实际学习时间:23小时