无向图:一种边为无序结点对的图html
若是无向图拥有最大数目的连通顶点的边,则认为这个无向图是彻底的。java
- 对有n个顶点的无向图,要使图彻底就要求有n(n-1)/2条边。
若是无向图中的任意两个顶点之间都存在一条路径,则认为这个无向图是连通的。git
无向树是一种连通的无环无向图,其中一个元素被指定为树根。算法
有向图(双向图):边为有序顶点对的图,eg:边(A,B)容许从A向B游历,但不容许反方向的游历。数组
若是有向图的每两个顶点之间都有两条方向相反的边链接,则认为这个有向图是彻底的。网络
若是有向图的任意两个顶点之间都存在一条路径,且链接两个顶点的路径中全部的边都必须同向,则认为这个有向图是连通的。数据结构
有向树是一种指定了一个元素做为树根的有向图。学习
- 不存在其余顶点到树根的链接
- 每一个非树根元素刚好有一个链接
- 树根到每一个其余顶点都有一条路径
三元组来表示每条边:起始顶点、终止顶点、权重测试
- 无向网络的起始顶点与终止顶点能够互换
- 有向图必须包含每一个有向链接的三元组
广度优先遍历(相似树的层序遍历)优化
![]()
- 首先从一个未走到过的顶点做为起始顶点,好比元素0顶点做为起点。
- 沿0顶点的边去尝试访问其它未走到过的顶点,首先发现2顶点尚未走到过,因而来到了2顶点。
- 返回到0顶点,再以0顶点做为出发点继续尝试访问其它未走到过的顶点,这样来到了1顶点。
- 返回到0顶点,再以0顶点做为出发点继续尝试访问其它未走到过的顶点,这样来到了5顶点。
- 可是,此时沿0顶点的边,已经不能访问到其它未走到过的顶点了,因此须要返回到2顶点。
- 沿2顶点的边去尝试访问其它未走到过的顶点,(由于1顶点已经走过了)首先发现3顶点尚未走到过,因而来到了3顶点。
- 返回到2顶点,再以2顶点做为出发点继续尝试访问其它未走到过的顶点,这样来到了4顶点。
- 可是,此时沿4顶点的边,已经不能访问到其它未走到过的顶点了,至此,全部顶点咱们都走到过了,遍历结束。
深度优先遍历(相似树的前序遍历)
![]()
- 首先从一个未走到过的顶点做为起始顶点,好比元素0顶点做为起点。
- 沿0顶点的边去尝试访问其它未走到过的顶点,首先发现2顶点尚未走到过,因而来到了2顶点。
- 再以2顶点做为出发点继续尝试访问其它未走到过的顶点,这样又来到了1顶点。
- 再以1号顶点做为出发点继续尝试访问其它未走到过的顶点。
- 可是,此时沿1顶点的边,已经不能访问到其它未走到过的顶点了,因此须要返回到2顶点。
- 返回到2号顶点后,以2顶点做为出发点继续尝试访问其它未走到过的顶点,此时又会来到3顶点,再以3号顶点做为出发点继续访问其它未走到过的顶点,因而又来到了5号顶点。
- 可是,此时沿5顶点的边,已经不能访问到其它未走到过的顶点了,因此须要返回到3顶点。
- 返回到3顶点后,以3顶点做为出发点继续尝试访问其它未走到过的顶点,此时会来到4顶点,再以4顶点做为出发点继续访问其它未走到过的顶点。
- 可是,此时沿4顶点的边,已经不能访问到其它未走到过的顶点了,至此,全部顶点咱们都走到过了,遍历结束。
深度优先遍历和广度优先遍历的位移不一样是使用到了栈而不是队列来管理遍历。
最小生成树是边的权重总和和小于或等于同一个图中其余任何一棵生成树的权重总和。
- 非连通的无向图,不存在最小生成树
- 权重不必定和距离成正比
- 权重多是0或负数
- 若存在相等的权重,那么最小生成树可能不惟一
- Prim算法:
方法二:Dijkstra算法
以0顶点为开始位置,0顶点到1顶点的权重为3,到2顶点的权重为max,到3顶点的权重为4,到4顶点的权重为5。
选取权重最小的顶点为1顶点,由于全部的权重都为正,那么0顶点到1顶点的最短距离就是3,再以1顶点开始,1顶点能够到4顶点和2顶点,到4顶点的位置是3+1=4<5,即0顶点到4顶点的最短距离是经过1顶点的,再看到2顶点的位置是3+10=13<max,即0顶点直接到2顶点的距离为max,可是经过1顶点能够缩短为13。
扣除0顶点和1顶点已经肯定好了最短距离以后,选取最短的权重为0顶点到3顶点为4。
以3顶点开始,3顶点能够到2顶点,到2顶点的位置是4+7=11<13,即0顶点到2顶点的最短距离,从max到经过1顶点再到经过3顶点到2顶点。
扣除0顶点、1顶点和3顶点,已经肯定好了最短距离以后,选取最小的权重为0顶点到1顶点再到4顶点为4,4顶点能够到1顶点、3顶点和2顶点,可是3顶点和1顶点已经肯定好了,只剩2顶点,那么到2顶点的位置为3+1+6=10<11,即到2顶点的最短距离改成经过1顶点、4顶点最后到2顶点。
最后扣除0顶点、1顶点、3顶点和4顶点,只剩下2顶点,距离只剩10,因此到2顶点的最短距离为10,结束算法。
邻接列表
- 在邻接表的表示中,无向图的同一条边在邻接表中存储的两次。若是想要知道顶点的读,只须要求出所对应链表的结点个数便可。
- 有向图中每条边在邻接表中只出现一此,求顶点的出度只须要遍历所对应链表便可。求出度则须要遍历其余顶点的链表。
无向图的邻接列表:
有向图的邻接列表:
邻接矩阵
- 在邻接矩阵表示中,无向图的邻接矩阵是对称的。矩阵中第 i 行或 第 i 列有效元素个数之和就是顶点的度。
- 在有向图中 第 i 行有效元素个数之和是顶点的出度,第 i 列有效元素个数之和是顶点的入度。
无向图的邻接矩阵:
有向图的邻接矩阵:
- 邻接矩阵与邻接表优缺点:邻接矩阵的优势是能够快速判断两个顶点之间是否存在边,能够快速添加边或者删除边。而其缺点是若是顶点之间的边比较少,会比较浪费空间。由于是一个 n∗nn∗n 的矩阵。而邻接表的优势是节省空间,只存储实际存在的边。其缺点是关注顶点的度时,就可能须要遍历一个链表。还有一个缺点是,对于无向图,若是须要删除一条边,就须要在两个链表上查找并删除。
边集数组
- 边集数组由两个一维数组构成:一个存储顶点信息; 一个存储边的信息,这个边数组每一个数据元素由一条边的起点下标(begin)、终点下标(end)、和权(weight)组成。
- 边集数组关注的是边的集合,在边集数组中要查找一个顶点的度须要扫描整个边数组,效率并不高。所以它更适合对边依次进行处理的操做,而不适合对顶点相关的操做。
无向图的边集数组:
有向图的边集数组:
十字链表
- 十字链表是为了便于求得图中顶点的度(出度和入度)而提出来的。用十字链表来存储有向图,能够达到高效的存取效果。它是综合邻接表和逆邻接表形式的一种链式存储结构。
绿色链表表示以结点A为弧头的弧组成的链表。黄色链表表示以结点A为弧尾的弧组成的链表。
邻接多重表
- 邻接多重表主要用于存储无向图。若是用邻接表存储无向图,每条边的两个边结点分别在以该边所依附的两个顶点为头结点的链表中,这给图的某些操做带来不便。所以,在进行这一类操做的无向图的问题中采用邻接多重表做存储结构更为适宜。
问题1解决方案:无向图的连通性在书上已经给出,字面理解就是任意两点之间都存在一条路径,而从编写的角度就是一个顶点开始深度优先遍历或是广度优先遍历得出的顶点数。而有向图的是具备方向的,若是这样的话咱们如何断定有向图的连通性?在查询的过程当中,发现有向图的连通性能够分红强连通图、弱连通图以及单向连通图。书上的图15.4的连通图就是一个弱连通图,若是是强连通图则须要在此基础上每两点之间都添加一条与之方向相反的线。针对有向图的测试连通性的方法能够用到Tarjan算法
- 在有向图中, 若对于任意两个两点之间, 都存在互逆的路径,则称此图是强连通图。即有向图中,若对于任意两个不一样的顶点x和y,都存在从x到y以及从y到x的路径,则称G是强连通图。
- 有向图的全部的有向边替换为无向边,所获得的图称为原图的基图。若是一个有向图的基图是连通图,则有向图是弱连通图。
- 若是有向图中,对于任意节点v1和v2,至少存在从v1到v2和从v2到v1的路径中的一条,则原图为单向连通图。
- 强连通图、连通图、单向连通图三者之间的关系是,强连通图必然是单向连通的,单向连通图必然是弱连通图。
- 在Tarjan算法中为每一个节点i维护了如下几个变量:
DFN[i]:深度优先搜索遍历时节点i被搜索的次序。
low[i]:节点i可以回溯到的最先位于栈中的节点。
flag[i]:标记几点i是否在栈中。- Tarjan算法的运行过程:
(1)首先就是按照深度优先搜索算法搜索的次序对图中全部的节点进行搜索。
(2)在搜索过程当中,对于任意节点u和与其相连的节点v,根据节点v是否在栈中来进行不一样的操做:
a.节点v不在栈中,即节点v尚未被访问过,则继续对v进行深度搜索。
b.节点v已经在栈中,即已经被访问过,则判断节点v的DFN值和节点u的low值的大小来更新节点u的low值。若是节点v的 DFN值要小于节点u的low值,根据low值的定义(可以回溯到的最先的已经在栈中的节点),咱们须要用DFN值来更新u 的low值。
(3)在回溯过程当中,对于任意节点u用其子节点v(其实不能算是子节点,只是在深度遍历的过程当中,v是在u以后紧挨着u的节点)的 low值来更新节点u的low值。由于节点v可以回溯到的已经在栈中的节点,节点u也必定可以回溯到。由于存在从u到v的直接路径,因此v可以到的节点u也必定可以到。
(4)对于一个连通图,咱们很容易想到,在该连通图中有且仅有一个节点u的DFN值和low值相等。该节点必定是在深度遍历的过程当中,该连通图中第一个被访问过的节点,由于它的DFN值和low值最小,不会被该连通图中的其余节点所影响。
public void DFSRecursion() { boolean[] visited = new boolean[numVertices]; for (int i = 0; i < numVertices; i++) visited[i] = false; for(int i=0;i<numVertices;i++) { if (!visited[i]) { depthFirstSearch(visited,i); } } } private void depthFirstSearch(boolean[] isVisited,int i) { System.out.print(vertices[i]+" "); isVisited[i]=true; int w=getFirstNeighbor(i); while (w!=-1) { if (!isVisited[w]) { depthFirstSearch(isVisited,w); } w=getNextNeighbor(i, w); } } public void broadFirstSearch() { boolean[] visited = new boolean[numVertices]; for (int i = 0; i < numVertices; i++) visited[i] = false; for(int i=0;i< numVertices;i++) { if(!visited[i]) { broadFirstSearch(visited, i); } } } private void broadFirstSearch(boolean[] isVisited,int i) { int u,w; LinkedList queue=new LinkedList(); System.out.print(vertices[i]+" "); isVisited[i]=true; queue.addLast(vertices[i]); while (!queue.isEmpty()) { u=((Integer)queue.removeFirst()).intValue(); w=getFirstNeighbor(u); while(w!=-1) { if(!isVisited[w]) { System.out.print(vertices[w]+" "); isVisited[w]=true; queue.addLast(w); } w=getNextNeighbor(u, w); } } }
问题2的解决方案:根据题目要求容许客户输入两个城市以及两个城市之间的价格,这就相似加权图,根据GraphADT接口以及实现的添加顶点和添加边的方法都没有涉及到权重(这里的权重为两个城市之间的价格),而无向图实现的是肯定两个顶点之间是否有边,用一个二维布尔型数组存储对应顶点是否有边,咱们的想法是能够将是否有边来改成数字,用票价来实现是否有联系。那么问题又来了,没有联系的如何来界定?定义为最大仍是最小?若是定义为最小为零的话,咱们的选择就要在排除零的前提下进行Dijkstra算法,可是若是咱们将无联系的看做是无穷大的状况下,就会产生不同的效果,在选取权重较小即最便宜路径的时候就会很方便,并且事实上提供的算法也是要将无联系的设为最大,就如同教材内容总结部分叙述的算法同样。可是,不管是无穷大仍是无穷小可能是用什么表示?和数序符号同样么?API提供了解决办法(侯泽洋同窗提供):Double.POSITIVE_INFINITY
表示的是正无穷大值,Double.NEGATIVE_INFINITY
表示的是负无穷大值。这样就能够实现了,可是Dijkstra算法如何书写呢?又是一个问题好丧...在网上查了几个,没有适用成功因此经过看侯泽洋的博客找到了一个不错的博客,用他的算法来实现的具体代码,并且侯泽洋的代码层次设计的很是好,实现的也干净利落,很强大,是我值得学习的。此外,最短路径就能够用到Graph内的shortestPathLength
方法和iteratorShortestPath
实现,我在此基础上经过返回最短路径的数值,若是是1的话就会存在直达列车,大于1的话就会是不可直达,在同经过遍历最短路径的迭代方法进行输出最短路径就好。
错题已经在上周博客写过...
本周结对学习状况
20172314方艺雯
20172323王禹涵
结对学习内容:图和一堆算法
第十五章的图感受和红黑树同样,要构造很乱的数据结构来实现,此外还有一堆算法能够优化图的操做(算法做用很好,可是难于理解,有的思路就难理解)。好在有些代码给出了,可是书上的遍历代码就没给全,致使始终读不懂一个方法具体干吗的,具体实现的策略也不一样于以前的数据结构。要保持好的心态来学习代码,切莫急功近利...
代码行数(新增/累积) | 博客量(新增/累积) | 学习时间(新增/累积) | 重要成长 | |
---|---|---|---|---|
目标 | 5000行 | 30篇 | 400小时 | |
第一周 | 0/0 | 1/1 | 15/15 | |
第二周 | 703/703 | 1/2 | 20/35 | |
第三周 | 762/1465 | 1/3 | 20/55 | |
第四周 | 2073/3538 | 1/4 | 40/95 | |
第五周 | 981/4519 | 2/6 | 40/135 | |
第六周 | 1088/5607 | 2/8 | 50/185 | |
第七周 | 1203/6810 | 1/9 | 50/235 | |
第八周 | 2264/9074 | 2/11 | 50/285 | |
第九周 | 2045/11119 | 1/12 | 50/335 |