20172313 2018-2019-1 《程序设计与数据结构》第九周学习总结
教材学习内容总结
无向图
- 无向图:与树相似,图也由结点和这些结点之间的链接构成。这些结点是顶点,而结点之间的连接是边。无向图是一种边为无序结点对的图。因而,记作(A,B)的边就意味着A与B之间有一条从两个方向均可以游历的链接。边记做(A,B)和记做(B,A)的含义是彻底同样的。
- 彻底图:若是一个无向图含有最多条边,那么它为彻底图。例如:
- 彻底图有n(n-1)条边。(此时假设没有边自循环。)
- 链接图中两个顶点的边的序列,能够由多条边组成。(图中的路径是双向的。)
- 路径长度:路径中所含边的数目(顶点个数减1)。
- 连通图:无向图中任意两个顶点之间都存在一条路径。(彻底图必定是连通图,连通图不必定是彻底图。)

- 环路:一种首顶点和末顶点相同且没有重边的路径。

- 无环图:没有环路的图。
- 无向图是一种连通的无环无向图,其中一个元素被指定为树根。
有向图
- 有向图:又称双向图,是一种边为有序顶点对的图。(边(A,B)和边(B,A)方向不一样)
- 有向路径:链接两个顶点有向边的序列。
- 注意:注意连通图的有向图和无向图之间的不一样:
- 上图第一个图是连通的,第二个图并非连通的,由于没有任何路径能从其余顶点游历到顶点A。
- 拓扑序:有向图中没有环路,且有一条从A到B的边,则能够吧顶点A安排在顶点B以前,这种排列获得的顶点次序。
- 有向树是一个有向图,其中指定一个元素为根,则具备下列特性:
- 不存在其余顶点到树根的链接。
- 每一个非树根元素刚好有一个链接。
- 树根到每一个其余顶点都有一条路径。
网络
- 网络:又称加权图,每条边都对应一个权值(数据信息)的图,能够是有向的也能够是无向的。(城市之间的航线、票价等)
- 网络的边由起始顶点、终止定点和权重构成的三元组来表示。
经常使用的图算法
- 深度优先遍历:和树的先序遍历比较相似。假设初始状态是图中全部顶点均未被访问,则从某个顶点v出发,首先访问该顶点,而后依次从它的各个未被访问的邻接点出发深度优先搜索遍历图,直至图中全部和v有路径相通的顶点都被访问到。 若此时尚有其余顶点未被访问到,则另选一个未被访问的顶点做起始点,重复上述过程,直至图中全部顶点都被访问到为止。
- 无向图示例以下:
- ①访问A。
- ②访问(A的邻接点)C。在第1步访问A以后,接下来应该访问的是A的邻接点,即"C,D,F"中的一个。但在本文的实现中,顶点ABCDEFG是按照顺序存储,C在"D和F"的前面,所以,先访问C。
- ③访问(C的邻接点)B。
在第2步访问C以后,接下来应该访问C的邻接点,即"B和D"中一个(A已经被访问过,就不算在内)。而因为B在D以前,先访问B。
- ④访问(C的邻接点)D。在第3步访问了C的邻接点B以后,B没有未被访问的邻接点;所以,返回到访问C的另外一个邻接点D。
- ⑤访问(A的邻接点)F。前面已经访问了A,而且访问完了"A的邻接点B的全部邻接点(包括递归的邻接点在内)";所以,此时返回到访问A的另外一个邻接点F。
- ⑥访问(F的邻接点)G。
- ⑦访问(G的邻接点)E。
- 所以访问顺序是:A -> C -> B -> D -> F -> G -> E
- 有向图示例以下:
- ①访问A。
- ②访问B。在访问了A以后,接下来应该访问的是A的出边的另外一个顶点,即顶点B。
- ③访问C。在访问了B以后,接下来应该访问的是B的出边的另外一个顶点,即顶点C,E,F。在本文实现的图中,顶点ABCDEFG按照顺序存储,所以先访问C。
- ④访问E。接下来访问C的出边的另外一个顶点,即顶点E。
- ⑤访问D。接下来访问E的出边的另外一个顶点,即顶点B,D。顶点B已经被访问过,所以访问顶点D。
- ⑥访问F。接下应该回溯"访问A的出边的另外一个顶点F"。
- ⑦访问G。
- 所以访问顺序是:A -> B -> C -> E -> D -> F -> G
- 广度优先遍历
- 又称为"宽度优先搜索"或"横向优先搜索",简称BFS。从图中某顶点v出发,在访问了v以后依次访问v的各个不曾访问过的邻接点,而后分别从这些邻接点出发依次访问它们的邻接点,并使得“先被访问的顶点的邻接点先于后被访问的顶点的邻接点被访问,直至图中全部已被访问的顶点的邻接点都被访问到。若是此时图中尚有顶点未被访问,则须要另选一个不曾被访问过的顶点做为新的起始点,重复上述过程,直至图中全部顶点都被访问到为止。换句话说,广度优先搜索遍历图的过程是以v为起点,由近至远,依次访问和v有路径相通且路径长度为1,2...的顶点。
- 无向图示例以下:
- ①访问A。
- ②依次访问C,D,F。在访问了A以后,接下来访问A的邻接点。前面已经说过,在本文实现中,顶点ABCDEFG按照顺序存储的,C在"D和F"的前面,所以,先访问C。再访问完C以后,再依次访问D,F。
- ③依次访问B,G。在第2步访问完C,D,F以后,再依次访问它们的邻接点。首先访问C的邻接点B,再访问F的邻接点G。
- ④访问E。在第3步访问完B,G以后,再依次访问它们的邻接点。只有G有邻接点E,所以访问G的邻接点E。
- 所以访问顺序是:A -> C -> D -> F -> B -> G -> E
- 有向图示例以下:
- ①访问A。
- ②访问B。
- ③依次访问C,E,F。在访问了B以后,接下来访问B的出边的另外一个顶点,即C,E,F。前面已经说过,在本文实现中,顶点ABCDEFG按照顺序存储的,所以会先访问C,再依次访问E,F。
- ④依次访问D,G。在访问完C,E,F以后,再依次访问它们的出边的另外一个顶点。仍是按照C,E,F的顺序访问,C的已经所有访问过了,那么就只剩下E,F;先访问E的邻接点D,再访问F的邻接点G。
- 所以访问顺序是:A -> B -> C -> E -> F -> D -> G
- 测试连通性:从任意结点开始的广度优先遍历中获得的顶点数等于图中所含顶点数。
- 最小生成树
- 生成树:生成树是一棵含有图中全部顶点和部分边(但可能不是全部边)的树。
- 最小生成树:所含边权值之和小于其余生成树的边的权值之和。计算最小生成树通常有两种算法:Kruskal和Prim算法
- Kruskal算法:此算法能够称为“加边法”,初始最小生成树边数为0,每迭代一次就选择一条知足条件的最小代价边,加入到最小生成树的边集合里。具体操做以下:
- ① 把图中的全部边按代价从小到大排序;
- ② 把图中的n个顶点当作独立的n棵树组成的森林;
- ③ 按权值从小到大选择边,所选的边链接的两个顶点ui,vi,应属于两颗不一样的树,则成为最小生成树的一条边,并将这两颗树合并做为一颗树。
- ④重复③,直到全部顶点都在一颗树内或者有n-1条边为止。

- Prim算法:此算法能够称为“加点法”,每次迭代选择代价最小的边对应的点,加入到最小生成树中。算法从某一个顶点s开始,直到最小生成树含有原始图的全部顶点时结束。具体操做以下:
- ①图的全部顶点集合为V;初始令集合u={s},v=V−u;
- ②在两个集合u,v可以组成的边中,选择一条代价最小的边(u0,v0),加入到最小生成树中,并把v0并入到集合u中。
- ③重复上述步骤,直到最小生成树有n-1条边或者n个顶点为止。

- 判断最短路径
- 第一种方法:断定起始顶点和目标顶点之间是否存在最短路径(两个顶点之间边数最少的路径)。
- 第二种方法:Dijkstra算法:设G=(V,E)是一个带权有向图,把图中顶点集合V分红两组,第一组为已求出最短路径的顶点集合(用S表示,初始时S中只有一个源点,之后每求得一条最短路径 , 就将加入到集合S中,直到所有顶点都加入到S中,算法就结束了),第二组为其他未肯定最短路径的顶点集合(用U表示),按最短路径长度的递增次序依次把第二组的顶点加入S中。在加入的过程当中,总保持从源点v到S中各顶点的最短路径长度不大于从源点v到U中任何顶点的最短路径长度。此外,每一个顶点对应一个距离,S中的顶点的距离就是从v到此顶点的最短路径长度,U中的顶点的距离,是从v到此顶点只包括S中的顶点为中间顶点的当前最短路径长度。具体操做以下;
- ①初始时,S只包含源点,即S={v},v的距离为0。U包含除v外的其余顶点,即:U={其他顶点},若v与U中顶点u有边,则<u,v>正常有权值,若u不是v的出边邻接点,则<u,v>权值为∞。
- ②从U中选取一个距离v最小的顶点k,把k,加入S中(该选定的距离就是v到k的最短路径长度)。
- ③以k为新考虑的中间点,修改U中各顶点的距离;若从源点v到顶点u的距离(通过顶点k)比原来距离(不通过顶点k)短,则修改顶点u的距离值,修改后的距离值的顶点k的距离加上边上的权。
- ④重复步骤b和c直到全部顶点都包含在S中。
- 示例以下,求从顶点v1到其余各个顶点的最短路径

首先第一步,咱们先声明一个dis数组,该数组初始化的值为:

这时顶点集合S中只有一个元素:S={v1},既然是求V1顶点到其他各个顶点的最短路径,那么咱们就先找到V1可以直接到达的点,从图中可知是V2和V3,咱们把dis[m]设为从顶点d[0]处到V(m+1)的路径查毒,同时把全部其余(顶点不能直接到达的)顶点的路径长度设为无穷大。这时在数组中表示就为dis[1]=12,dis[2]=6。由于目前离 v1顶点最近的是 v3顶点,而且这个图全部的权重都是正数,因此不可能经过第三个顶点中转,这时V1到V3的最短路程就是dis[2]值。将V3加入S中。
咱们如今已经肯定了一个点的最短路径,下面咱们来看V3能到哪些点,从图中看V3只能到V5且V1——V3——V5的距离为17,显而易见要比∞要小,因此数组更新以下。

此时V1和V3都已经存入S中,咱们又从除dis[2]和dis[0]外的其余值中寻找最小值,此时dis[1]最小,同理,V1到V2的最短距离就是dis[1]的值,将V2加入U中,从图中看V2能到V3和V4,前面分析过,V1到V3的距离是最短的,因此再也不看V2到V4,V1——V2——V4的距离为15,显而易见要比∞要小,因此数组更新以下。

此时V一、V二、V3都已经存入S中,咱们又从除dis[0]、dis[1]、dis[2]外的其余值中寻找最小值,此时dis[3]最小,将V4加入U中,从图中看V4只能到V5,有前面可知V1——V4的最短距离为15,因此V1——V4——V5的距离为16,比17小,因此对dis[4]进行替换
,数组更新以下

而后再次寻找最小值,将V5加入U中,计算完毕。
图的实现策略
- 邻接列表:将每一个顶点的邻接点串成一个单链表。

- 邻接矩阵: 逻辑结构分为两部分:Vexs[](存储顶点)和Arcs[][](邻接矩阵)集合。所以,用一个一维数组存放图中全部顶点数据;用一个二维数组存放顶点间关系(边或弧)的数据,这个二维数组称为邻接矩阵。无向图的邻接矩阵示例以下:

教材学习中的问题和解决过程
- 问题1:在学习教材的时候,最小生成树的方法中有这样一行代码“resultGraph[][] = Double.Positive_INFINITY”,不是很理解这里的POSITIVE_INFINITY表明什么意思。
- 问题1解决方案:咱们首先来思考这样一个问题,在进行浮点数运算的时候,有时咱们会遇到除数为0的状况,那咱们该如何解决呢?因此引入了无限这个概念,POSITIVE_INFINITY正是能够用来表明无限。示例以下
double i = Double.POSITIVE_INFINITY; // i表示为无限大
public static final double POSITIVE_INFINITY = 1.0 / 0.0;
- 问题2:不是很理解求最短路径时的Dijkstra算法。
- 问题2解决方案:前文有详细叙述。
- 问题3:不是很理解求最小生成树的Prim算法。
- 问题3解决方案:前文有详细叙述。
代码调试中的问题和解决过程
- 问题1:在作pp15_1的时候,对方法进行测试,陷入了死循环。

- 问题1解决方案:首先,我先要肯定是哪一个方法致使了死循环,因为我调用了toString方法,但屏幕上并无出现任何数值,这就说明要么是toString出现了问题,要么在它以前就出现了错误。从前日后分析,把全部的操做都注释掉,依次判断,发现了一个奇怪的现象,当第一次调用addEdge方法的时候是不会出现死循环的,在第二次的时候才会出现。虽然不知道具体在哪一个地方出现了问题,但能够确定的确是addEdge出了毛病。用debug对代码进行分析以后发现了问题,因为邻接列表的数据结构,利用列表中的元素的指针指向其余元素的时候不能直接指向该列表中的元素,而是要用一个temp的其值进行储存,再用指针指向它,这样问题就得以解决了。


- 问题2:在作pp15_7计算带权重的有向图的最短路径的时候没法获得指望值。
- 问题2解决方案:在输入的时候我想得到A到C的权重和最小的路径,却获得的值为49。由于个人思路是把原先判断两个顶点之间是否有边的adjMatrix的数组改为了存放两顶点之间边权重的数组。若是两顶点之间没有边,将边的值设置为-1,若是有边但没有存权重就把它设置为无穷大。出现的值是49,多是由于在计算的时候加上了-1,因此我就想应该要在哪里添加判断条件。

我在上图所示的地方加上了判断条件,但结果并无发生改变。判断条件是temp < dist[j],为dis[]加限定条件只是限制了数组值的范围,并不会致使出现49的状况。还要对adjMaxtrix[][]进行限制。以下图所示,问题就得以解决了
for (int j = 0; j < numVertices; j++) {
if (adjMatrix[k][j] != -1&&dist[j]!= -1) {
double temp = (adjMatrix[k][j] == Double.POSITIVE_INFINITY
? Double.POSITIVE_INFINITY : (min + adjMatrix[k][j]));
if (flag[j] == false && (temp < dist[j])) {
dist[j] = temp;
previous[j] = k;
}
}
}


上周考试错题总结
这周没有错题哦~算法
结对及互评
- 博客中值得学习的或问题:
- 排版精美,对教材的总结细致,善于发现问题,对于问题研究得很细致,解答也很周全。
- 代码中值得学习的或问题:
点评过的同窗博客和代码
其余(感悟、思考等,可选)
刚刚感受上周的内容简单了一些,本章的内容又给了我一闷棍,本章的难度不只难在算法的理解上,还难在代码的实现上,光是学习算法就花费了很多时间,关键代码的理解也不容易。但这周学下来整体上说收获仍是挺大的,这周的学习状态也还算能够,但愿本身可以继续保持这样的状态,继续进步吧!数组
学习进度条
第一周 |
200/200 |
1/1 |
5/20 |
|
第二周 |
981/1181 |
1/2 |
15/20 |
|
第三周 |
1694/2875 |
1/3 |
15/35 |
|
第四周 |
3129/6004 |
1/4 |
15/50 |
|
第五周 |
1294/7298 |
1/5 |
15/65 |
|
第六周 |
1426/8724 |
1/6 |
20/85 |
|
第七周 |
2071/10795 |
1/7 |
20/105 |
|
第八周 |
3393/14188 |
1/8 |
20/125 |
|
第九周 |
3217/17045 |
1/9 |
20/145 |
|
计划学习时间:20小时网络
实际学习时间:20小时数据结构
参考资料