图是网络结构的抽象模型,其是一组由边连接的节点。
由一条边连接在一起的顶点称为相邻顶点; 一个顶点的度是其相邻顶点的数量
路径是顶点v1,v2,v3,...vk的一个连续序列
简单路径要求不包含重复的顶点
如果一个图中不存在环,则称该图是无环的,如果图中每两个顶点间都存在路径,则该图是连通的。
有向图:边有方向;无向图:边没有方向
如果图中每两个顶点间在双向上都存在路径,则该图是强连通的。
也有加权的图和未加权的图,带权值的图如下:
图的表示:
邻接矩阵:每一个节点都和一个整数关联,该整数作为数组的索引,用一个二维数组来表示顶点间的连接,如果两节点连接,则对应二维数组值为1
邻接表:由图中每个顶点的相邻顶点列表组成。可以用数组、链表、散列表或字典来表示相邻顶点列表。
关联矩阵:矩阵的行表示顶点,矩阵的列表示边。用二维数组来表示两者的连通性。
图类的JavaScript实现:
function Graph() {
let vertices = []; //list
let adjList = new Dictionary();
this.addVertex = function(v){
vertices.push(v);
adjList.set(v, []); //initialize adjacency list with array as well;
};
this.addEdge = function(v, w){
adjList.get(v).push(w);
//adjList.get(w).push(v); //commented to run the improved DFS with topological sorting
};
this.toString = function(){
let s = '';
for (var i=0; i<vertices.length; i++){
s += vertices[i] + ' -> ';
let neighbors = adjList.get(vertices[i]);
for (var j=0; j<neighbors.length; j++){
s += neighbors[j] + ' ';
}
s += '\n';
}
return s;
};
let initializeColor = function(){
let color = [];
for (var i=0; i<vertices.length; i++){
color[vertices[i]] = 'white';
}
return color;
};
this.bfs = function(v, callback){ //广度优先搜索
//创建一个队列Q;将v标注为被发现的(灰色),并将v入队列Q;
// 如果Q非空,则进行以下步骤:(1)、将u从Q中出队列 (2)、将标注u为被发现的(灰色)
// (3)、将u所有未被访问的邻点(白色)入队列 (4)、将u标注为已被探索的(黑色)
let color = initializeColor(),
queue = new Queue();
queue.enqueue(v);
while (!queue.isEmpty()){
let u = queue.dequeue(),
neighbors = adjList.get(u);
color[u] = 'grey';
for ( let i=0; i<neighbors.length; i++){
let w = neighbors[i];
if (color[w] === 'white'){
color[w] = 'grey';
queue.enqueue(w);
}
}
color[u] = 'black';
if (callback) {
callback(u);
}
}
};
this.dfs = function(callback){
let color = initializeColor();
for ( let i=0; i<vertices.length; i++){
if (color[vertices[i]] === 'white'){
dfsVisit(vertices[i], color, callback);
}
}
};
var dfsVisit = function(u, color, callback){
color[u] = 'grey';
if (callback) {
callback(u);
}
console.log('Discovered ' + u);
let neighbors = adjList.get(u);
for ( let i=0; i<neighbors.length; i++){
let w = neighbors[i];
if (color[w] === 'white'){
dfsVisit(w, color, callback);
}
}
color[u] = 'black';
console.log('explored ' + u);
};
this.BFS = function(v){
let color = initializeColor(),
queue = new Queue(),
d = [],
pred = [];
queue.enqueue(v);
for ( let i=0; i<vertices.length; i++){
d[vertices[i]] = 0;
pred[vertices[i]] = null;
}
while (!queue.isEmpty()){
let u = queue.dequeue(),
neighbors = adjList.get(u);
color[u] = 'grey';
for (i=0; i<neighbors.length; i++){
let w = neighbors[i];
if (color[w] === 'white'){
color[w] = 'grey';
d[w] = d[u] + 1;
pred[w] = u;
queue.enqueue(w);
}
}
color[u] = 'black';
}
return {
distances: d,
predecessors: pred
};
};
let time = 0;
this.DFS = function(){
let color = initializeColor(),
d = [],
f = [],
p = [];
time = 0;
for ( let i=0; i<vertices.length; i++){
f[vertices[i]] = 0;
d[vertices[i]] = 0;
p[vertices[i]] = null;
}
for (i=0; i<vertices.length; i++){
if (color[vertices[i]] === 'white'){
DFSVisit(vertices[i], color, d, f, p);
}
}
return {
discovery: d,
finished: f,
predecessors: p
};
};
var DFSVisit = function(u, color, d, f, p){
console.log('discovered ' + u);
color[u] = 'grey';
d[u] = ++time;
let neighbors = adjList.get(u);
for (var i=0; i<neighbors.length; i++){
let w = neighbors[i];
if (color[w] === 'white'){
p[w] = u;
DFSVisit(w,color, d, f, p);
}
}
color[u] = 'black';
f[u] = ++time;
console.log('explored ' + u);
};
}
广度优先搜索(Breadth-First Search, BFS):从指定的第一个顶点开始遍历图,先访问其所有的相邻点,一层一层访问,可以理解为先宽后深地访问顶点。
深度优先搜索(Depth-First Search, DFS):从第一个指定的顶点开始遍历图,沿着路经直到这条路径的最后一个顶点被访问,接着园路返回并探索下一条路径,就是先深度后广度地访问顶点。