定义:图由边的集合及顶点的集合组成。顶点也有权重, 也称为成本。javascript
若是一个图的顶点对是有序的, 则能够称之为有向图。在对有向图中的顶点对排序后, 即可以在两
个顶点之间绘制一个箭头。 有向图代表了顶点的流向。java
若是图是无序的, 则称之为无序图, 或无向图。算法
图中的一系列顶点构成路径, 路径中全部的顶点都由边链接。 路径的长度用路径中第一个顶点到最后一个顶点之间边的数量表示。 由指向自身的顶点组成的路径称为环, 环的长度为 0。数组
圈是至少有一条边的路径, 且路径的第一个顶点和最后一个顶点相同。 不管是有向图仍是无向图, 只要是没有重复边或重复顶点的圈, 就是一个简单圈。 除了第一个和最后一个顶点之外, 路径的其余顶点有重复的圈称为平凡圈。数据结构
若是两个顶点之间有路径, 那么这两个顶点就是强连通的, 反之亦然。 若是有向图的全部的顶点都是强连通的, 那么这个有向图也是强连通的。函数
用Vertex类表示节点,Vertex 类有两个数据成员: 一个用于标识顶点, 另外一个是代表这个顶点是否被访问过的布尔值。它们分别被命名为 label 和 wasVisited。this
function Vertex(label){ this.label = label; }
用邻接表或邻接表数组来表示边。数组的索引表示顶点,元素是一个数组,里面的成员是与该顶点相连的其余顶点。所以邻接表是一个二维的数组。prototype
搜索图分两种方式:深度优先和广度优先。code
广度优先搜索算法:数据结构是队列。经过将顶点存入队列中,最早入队列的顶点先被探索。
深度优先搜索算法:数据结构是栈。经过将顶点存入栈中,沿着路径探索顶点,存在新的相邻顶点就去访问。排序
深度优先
深度优先搜索包括从一条路径的起始顶点开始追溯, 直到到达最后一个顶点, 而后回溯,继续追溯下一条路径, 直到到达最后的顶点, 如此往复, 直到没有路径为止。
思路:访问一个没有访问过的顶点, 将它标记为已访问, 再递归地去访问在初始顶点的邻接表中其余没有访问过的顶点。
广度优先
广度优先搜索从第一个顶点开始, 尝试访问尽量靠近它的顶点。 本质上, 这种搜索在图上是逐层移动的, 首先检查最靠近第一个顶点的层, 再逐渐向下移动到离起始顶点最远的层。
用JS实现以上两种搜索方法:
// 建立图类 function Graph(v){ this.vertices = v; // 共有多少个节点 this.edges = 0; // 有多少条边 this.adj = []; // 邻接表数组 for(var i = 0;i<this.vertices;i++){ this.adj[i] = []; } this.marked = []; // 用于搜索 for(var i = 0;i<this.vertices;i++){ this.marked[i] = false; } this.edgeTo = []; // 用于广度优先搜索查找最短路径 } Graph.prototype = { constructor: Graph, // 在两个顶点间画一条边 addEdge(v, w){ this.adj[v].push(w); this.adj[w].push(v); this.edges++; }, showGraph(){ for(var i = 0;i<this.vertices;i++){ var str = ''; for(var j = 0;j<this.vertices;j++){ if(this.adj[i][j] !== undefined){ str += this.adj[i][j] + ' '; } } console.log(i + "-> " + str + '\n'); } }, // 深度优先搜索函数depth first searching,递归实现 dfs(v){ this.marked[v] = true; if(this.adj[v].length){ console.log("Visited vertex: " + v); for(var w of this.adj[v]){ if(!this.marked[w]){ this.dfs(w); } } } }, // 广度优先搜索函数,无递归实现 bfs(s){ var queue = []; this.marked[s] = true; queue.push(s); while(queue.length > 0){ var v = queue.shift(); console.log("Visited vertex: " + v); for(var w of this.adj[v]){ if(!this.marked[w]){ this.edgeTo[w] = v; this.marked[w] = true; queue.push(w); } } } } }
利用广度优先搜索方法查找最短路径(查找顶点1和顶点4之间的最短路径):
须要再扩展一个方法,经过执行一次广度优先搜索后获得的edgeTo数组来找到1和4节点之间相互链接的节点:
Graph.prototype.pathTo = function(w, v){ this.bfs(w); var source = w; // 顶点w做为起点 if(!this.hasPathTo(v)){ return undefined; } var path = []; // 保存路径,不过是从终点到起点的 for(var i = v; i != source; i = this.edgeTo[i]){ path.push(i); } path.push(source); return path; } Graph.prototype.hasPathTo(v){ return this.marked[v]; } var g = new Graph(6); g.addEdge(0, 1); g.addEdge(0, 2); g.addEdge(1, 5); g.addEdge(2, 3); g.addEdge(3, 5); g.addEdge(2, 4); g.showGraph(); g.pathTo(4, 1); // [1, 0, 2, 4]