本周学习了第15章图
图和图论衔接了数学和计算机科学的整个分支学科php
若是无向图拥有最大数目的连通顶点的边,则认为这个无向图是彻底的html
对有n个顶点的无向图,要使该图是彻底的,要求有n(n-1)/2条边node
环路是一种首顶点和末顶点相同且没有重边的路径。无向树是一种连通的无环无向图,其中一个元素被指定为树根。git
若是有向图中没有环路,且有一条从A到B的边,则能够把顶点A安排在顶点B以前,这样排列获得的顶点次序称为拓扑序。web
- 有向树是一种指定了一个元素作为树根的有向图,该图含有如下性质
- 不存在其余顶点到树根的链接
- 每一个非树根元素刚好有一个链接
- 树根到每一个其余顶点都有一条路径
adjMatrix[index1][index2] = true, adjMartix[index2][index1] = true
。问题3解决方案:广度优先遍历根据书上的代码已经很好理解了,从任意一个顶点开始,而后分别找该顶点相邻的顶点。可是深度优先遍历相似于前序遍历,这种状况下也是去找该顶点的左右顶点吗?
查阅资料,有人很形象地把深度优先遍历总结成了一句话“一路走到头,不撞墙不回头”,这个意思就是说,遍历以一个未被访问过的顶点做为起始顶点,沿当前顶点的边走到未访问过的顶点,当没有未访问过的顶点时,则回到上一个顶点,继续试探别的顶点,直到全部的顶点都被访问过。算法
以上图为例,以v1为起始顶点,首先走v2-->v4-->v8-->v5,而后没有能够访问的顶点了,返回到v1,再按照一路走到底的规则,走v3-->v6-->v7,遍历完成。编程
如今来看代码数组
public Iterator iteratorBFS(T startVertex) { return iteratorBFS(getIndex(startVertex)); }//公有方法,一样是找到起始顶点在顶点列表中的索引 private Iterator iteratorBFS(int startIndex) { Integer x; Queue<Integer> tq = new LinkedQueue<Integer>(); UnorderedListADT<T> rls = new ArrayUnoderedQueue<T>();//广度优先遍历须要一个队列和一个无序列表 if (!indexIsValid(startIndex)) { return rls.iterator(); }//判断索引值是否合法,若是不合法,返回一个rls的迭代,显示出来就是空 boolean[] visited = new boolean[numVertices]; for (int i = 0; i < numVertices; i++) { visited[i] = false; }//建立一个存储量为图的顶点数大小的布尔值数组,将数组内每个值都设置为false,表示未访问过 tq.enqueue(new Integer(startIndex));//将起始顶点的值存在队列中 visited[startIndex] = true;//将起始顶点对应的在布尔值数组中的位置设为true,表示已访问 while (!tq.isEmpty()) { // 出队列 x = tq.dequeue(); // 遍历记录表 rls.addToRear(vertices[x.intValue()]);//将每个已访问的顶点值存入无序列表中 for (int i = 0; i < numVertices; i++) { if (adjMatrix[x.intValue()][i] && !visited[i]) {//这里能够用邻接矩阵来表示它的相邻顶点,由于若是两个顶点是连通的的话,那么adjMatrix[x.intValue()][i]将会为true,因此这里的intValue方法应该也是定位数字的索引值 tq.enqueue(new Integer(i)); visited[i] = true; }//将与该顶点相邻且未访问过的顶点按照以前的顺序,先存入队列中,而后将其在visited中的对应位置改成true,直到队列不为空,遍历结束 } } return new GraphIterator(rls.iterator());
深度优先遍历与广度优先遍历的代码思想基本是相同的,但前者运用的是栈,进出的原则不一样因此输出的顺序有差异。同时,另外一点区别是,在顶点还没有添加到无序列表以前,不会将顶点设置为已访问网络
问题1:关于最小生成树“mstNetwork”这一优雅算法的不优雅理解数据结构
public Graph nstNetwork() { int x, y; int index; int weight; int[] edge = new int[2]; HeapADT<Double> minHeap = new LinkedHeap<Double>(); Network<T> resultGraph = new Network<T>(); if (isEmpty() || !isConnected()) return resultGraph;//当图是空的或者没有连通的时候返回一个空的引用 resultGraph.adjMatrix = new boolean[numVertices][numVertices];//建立一个邻接矩阵 for (int i = 0; i < numVertices; i++) for (int j = 0; j < numVertices; j++) resultGraph.adjMatrix[i][j] = Double.POSITIVE_INFINITY; resultGraph.vertices = (T[])(new Object[numVertices]); boolean[] visited = new boolean[numVertices]; for (int i = 0; i < numVertices; i++) visited[i] = false; //把全部的顶点都设置为未访问过的 edge[0] = 0; resultGraph.vertices[0] = this.vertices[0]; resultGraph.numVertices++; visited[0] = true; // Add all edges that are adjacent to vertex 0 to the stack. for (int i = 0; i < numVertices; i++) { minHeap.addElement(new Double(adjMatrix[0][i])) }//将全部含起始顶点的边按照权重次序添加到最小堆中 while ((resultGraph.size() < this.size()) && !minHeap.isEmpty()) { // Pop an edge off the stack and add it to the resultGraph. do{ weight = (minHeap.removeMin()).doubleValue(); edge = getEdgeWithWeightOf(weight, visited); }//从堆中取出最小边 while(!indexIsValid(edge[0]) || !indexIsValid(edge[1])); x = edge[0]; y = edge[1]; if (!visited[x]) index = x; if (!visited[y]) index = y; //从最小边中的两个顶点中选出未访问的一个顶点,并将索引值赋给index resultGraph.vertices[index] = this.vertices[index]; visited[index] = true; resultGraph.numVertices++;//将新顶点的信息赋给相应数组 resultGraph.adjMatrix[x][y] = this.adjMatrix[x][y]; resultGraph.adjMatrix[y][x] = this.adjMatrix[y][x]; // 添加全部含该新顶点且另外一顶点尚不在最小生成树中的边,直到最小树中包含图中的全部顶点或者最小堆中已经没有元素 for (int i = 0; i < numVertices; i++) { if (!visited[i] && this.adjMatrix[i][index] < Double.POSITIVE_INFINITY) { edge[0] = index; edge[1] = i; minHeap.addElement(new Double(adjMatrix[index][i])); } } } return resultGraph; }
目前还不太可以理解edge[]的使用以及其中的部分代码所起的做用
按照教材给出的提示,edge[]彷佛是,表示每条边的一个三元数组,其中包含起始顶点、终止顶点和权重,可是这里的定义又是int[] edge = new int[2]
,里面只包含了两个存储空间,照理说就不是用做存储边的数组了。包括其余一些代码的问题,好比一开始设置的resultGraph.adjMatrix[i][j] = Double.POSITIVE_INFINITY;
是什么意思。算法的核心思想大概是理解了,代码实现好像还差的很远。
--- 分割线 ---
以上问题的回答有参照同窗的博客以及与同窗的讨论。
首先Double.POSITIVE_INFINITY表示的是无限大,resultGraph.adjMatrix[i][j] = Double.POSITIVE_INFINITY;
在这里起到的做用,就和一个Boolean型的邻接矩阵,将它的初始值所有设置为false是一个道理。因此整个代码的前面一部分,所有是在定义实现最小生成树所须要的变量。除了刚才提到的邻接矩阵以外,还包含了一个存储访问信息的Boolean数组--visited,存储起始顶点和终止顶点索引值信息的数组edge,存储每个顶点信息的数组resultGraph.vertices = (T[])(new Object[numVertices]);
。
该最小生成树的生成的起始顶点,默认为了邻接矩阵的索引值为0的元素,因此首先设置
edge[0] = 0; resultGraph.vertices[0] = this.vertices[0]; resultGraph.numVertices++; visited[0] = true;
将全部数组都从起始顶点开始,利用for循环将含起始顶点的边的权重值依次添加进最小堆中,再依据最小堆的性质将权重最小的数弹出,据此找到相应的最小边,再根据最小边找到两个顶点中未访问的那一个顶点,并以此为新的起始顶点,去找下一个最小边,直到最小树中含有图中的全部顶点为止,也就是resultGraph.size() < this.size()) && !minHeap.isEmpty()
时
以上的分析都是基于我本身的理解,有一些部分我也没琢磨明白,还有不完备的地方,但愿多多指正
基于评分标准,我给谭鑫的博客打分:5分。得分状况以下:
正确使用Markdown语法(加1分)
模板中的要素齐全(加1分)
教材学习中的问题和解决过程, 一个问题加1分
代码调试中的问题和解决过程, 两个问题加2分
基于评分标准,我给方艺雯的博客打分:8分。得分状况以下
正确使用Markdown语法(加1分):
模板中的要素齐全(加1分)
教材学习中的问题和解决过程, 两个问题加2分
代码调试中的问题和解决过程, 四个问题加4分
书上的代码愈来愈很差理解了,而后本周开始要进行结对的小组编程,抱紧大腿,尽力跟上你们的步伐
代码行数(新增/累积) | 博客量(新增/累积) | 学习时间(新增/累积) | 重要成长 | |
---|---|---|---|---|
目标 | 5000行 | 30篇 | 400小时 | |
第一周 | 0/0 | 1/1 | 8/8 | |
第二周 | 470/470 | 1/2 | 12/20 | |
第三周 | 685/1155 | 2/4 | 10/30 | |
第四周 | 2499/3654 | 2/6 | 12/42 | |
第六周 | 1218/4872 | 2/8 | 10/52 | |
第七周 | 590/5462 | 1/9 | 12/64 | |
第八周 | 993/6455 | 1/10 | 12/76 | |
第九周 | 1192/7467 | 2/12 | 10/86 | |
第十周 | 1375/8842 | 1/13 | 12/98 |