参考书籍:《数据结构与算法JavaScript描述》
1. 图
图由边的集合以及顶点的集合组成。例如游戏中的地图,上面含有路线以及地点。地点就可以看作是顶点,路线看作是边。边将顶点连接起来,构成一条条路径。
2. 构建图
function Graph(v) {
this.vertices = v; // 顶点个数
this.edges = 0; // 边的条数
this.verticesRe = []; // 二维数组,用以存储与当前顶点相邻的顶点们
this.bmarked = []; // 标记数组
this.dmarked = []; // 标记数组
for(var i = 0; i < v; ++i) {
this.verticesRe[i] = [];
this.bmarked[i] = false;
this.dmarked[i] = false;
}
this.addEdge = addEdge;
}
this.dfs = dfs; // 深度优先搜索
this.bfs = bfs; // 广度优先搜索
function addEdge(v, w) {
this.verticesRe[v].push(w);
this.verticesRe[w].push(v);
this.edges++;
}
// 初始化图
var g = new Graph(6);
现在图已经初始化好,但缺少顶点跟边。可以按照这张图来添加顶点:
g.addEdge(0, 1);
g.addEdge(1, 2);
g.addEdge(1, 3);
g.addEdge(2, 4);
g.addEdge(3, 4);
g.addEdge(4, 5);
可以写一个展示顶点的方法来查看图:
function viewGraph() {
for (var i = 0; i < this.vertices; ++i) {
var temp= this.verticesRe[i];
for (var j = 0; j < temp.length ; ++j ) {
console.log(i + '=>' + temp[j]);
}
}
}
...
打印结果如下:
3. 深度优先搜索:
深度优先搜索是根据给定的一个顶点V,然后向着这个顶点的某一邻接未访问顶点前进,直到到达该路径最深处(即该路径已经没有未访问顶点),然后回溯,继续出发,依次对图的顶点进行深度优先遍历。
function dfs(v) {
this.dmarked[v] = true;
console.log(v);
var temp = this.verticesRe[v];
for(var i = 0; i < temp.length; ++i) {
if(!this.dmarked[temp[i]]) {
this.dfs(temp[i]);
}
}
}
...
g.dfs(0);
打印结果:
- 但这里的搜索顺序并不是唯一的,可以根据加入顶点的顺序来调整;
- 如果创建一个栈,在dfs函数最后将v压入栈中,再依次弹出,弹出的数字构成了一个拓扑序列。
4. 广度优先搜索:
广度优先搜索从给定的顶点开始,依次遍历它的邻接顶点,然后把这些邻接顶点当作出发顶点,再依次遍历它们的邻接顶点,直到图的最底层。设计的时候,可以添加一个队列,从第一个顶点开始,将它放入队列中,然后寻找它的邻接点,依次入队…这样就能保证遍历的顺序。
function bfs(v) {
var queue = [];
queue.push(v);
this.bmarked[v] = true;
while(queue.length) {
var shift = queue.shift(); //取出队列的第一个元素
console.log(shift);
var temp = this.verticesRe[shift];
for(var i = 0; i < temp.length; ++i) {
if(!this.bmarked[temp[i]]) {
this.bmarked[temp[i]] = true;
queue.push(temp[i]);
}
}
}
}
...
g.bfs(0);
打印结果: