概念
算法 | 数据结构 | 描述 |
---|---|---|
广度优先搜索 | 队列 | 通过将顶点存入队列中,最先入队列的顶点先被探索 |
深度优先搜索 | 栈 | 通过将顶点存入栈中,顶点是沿着路径被探索的,存在新的相邻顶点就去访问 |
当要标注已经访问过的顶点时,我们用三种颜色来反映它们的状态。
白色:表示该顶点还没有被访问。
灰色:表示该顶点被访问过,但并未被探索过。
黑色:表示该顶点被访问过且被完全探索过。
效果图
广度优先搜索(Breadth-First Search,BFS)) | 深度优先搜索(Depth-First Search,DFS) |
---|---|
![]() | ![]() |
无向图的实现(邻接矩阵、邻接表)
JavaScript代码实现如下,其中广度优先搜索函数是 bfs,深度优先搜索函数是 dfsStack 。
邻接矩阵 | 邻接表 |
---|---|
![]() | ![]() |
邻接表实现无向图
无向图的代码实现,此处,无线图的实现,采用了邻接表:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<!--支撑图的数据结构-->
<script src="struct.js"></script>
</head>
<body>
</body>
<script>
function Graph() {
var vertices = [];//点
var adjList = new Dictionary();//邻接表
this.addVertex = function (v) {
vertices.push(v);
adjList.set(v, []);
};
this.addEdge = function (v, w) {
adjList.get(v).push(w);
adjList.get(w).push(v);
};
this.toString = function () {
var s = '';
for (var i = 0; i < vertices.length; i++) {
s += vertices[i] + ' -> ';
var neighbors = adjList.get(vertices[i]);
for (var j = 0; j < neighbors.length; j++) {
s += neighbors[j] + ' ';
}
s += '\n';
}
return s;
};
var initializeColor = function () {
var color = [];
for (var i = 0; i < vertices.length; i++) {
color[vertices[i]] = 'white';
}
return color;
};
this.bfs = function (v, callback) {
var color = initializeColor(),
queue = new Queue();
queue.enqueue(v);
while (!queue.isEmpty()) {
var u = queue.dequeue(),
neighbors = adjList.get(u);
color[u] = 'grey';
for (var i = 0; i < neighbors.length; i++) {
var w = neighbors[i];
if (color[w] === 'white') {
color[w] = 'grey';
queue.enqueue(w);
}
}
color[u] = 'black';
if (callback) {
callback(u);
}
}
};
this.BFS = function (v) {
var color = initializeColor(),
queue = new Queue(),
d = [],
pred = [];
queue.enqueue(v);
for (var i = 0; i < vertices.length; i++) {
d[vertices[i]] = 0;
pred[vertices[i]] = null;
}
while (!queue.isEmpty()) {
var u = queue.dequeue(),
neighbors = adjList.get(u);
color[u] = 'grey';
for (i = 0; i < neighbors.length; i++) {
var 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
};
};
this.dfsStack = function (v, callback) {
var color = initializeColor(),
stack = new Stack();
stack.push(v);
while (!stack.isEmpty()) {
var u = stack.pop(),
neighbors = adjList.get(u);
color[u] = 'grey';
for (var i = 0; i < neighbors.length; i++) {
var w = neighbors[i];
if (color[w] === 'white') {
color[w] = 'grey';
stack.push(w);
}
}
color[u] = 'black';
if (callback) {
callback(u);
}
}
};
this.dfs = function (callback) {
var color = initializeColor();
for (var 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);
}
var neighbors = adjList.get(u);
for (var i = 0; i < neighbors.length; i++) {
var w = neighbors[i];
if (color[w] === 'white') {
dfsVisit(w, color, callback);
}
}
color[u] = 'black';
};
var time = 0;
this.DFS = function () {
var color = initializeColor(),
d = [],
f = [],
p = [];
time = 0;
for (var 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;
var neighbors = adjList.get(u);
for (var i = 0; i < neighbors.length; i++) {
var 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);
};
}
function printNode(value) {
console.log('Visited vertex: ' + value);
}
var graph = new Graph();
var myVertices = ['A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I'];
for (var i = 0; i < myVertices.length; i++) {
graph.addVertex(myVertices[i]);
}
graph.addEdge('A', 'B');
graph.addEdge('A', 'C');
graph.addEdge('A', 'D');
graph.addEdge('C', 'D');
graph.addEdge('C', 'G');
graph.addEdge('D', 'G');
graph.addEdge('D', 'H');
graph.addEdge('B', 'E');
graph.addEdge('B', 'F');
graph.addEdge('E', 'I');
//console.log(graph.toString());
graph.bfs(myVertices[0], printNode);
graph.dfsStack(myVertices[0], printNode);
</script>
</html>
邻接矩阵实现无向图
无向图的代码实现,此处,无线图的实现,采用了邻接矩阵
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<!--支撑图的数据结构-->
<script src="struct.js"></script>
</head>
<body>
</body>
<script>
var matrix = [
// A B C D E F G H I
// 0 1 2 3 4 5 6 7 8
/*A 0*/[0, 1, 1, 1, 0, 0, 0, 0, 0],
/*B 1*/[1, 0, 0, 0, 1, 1, 0, 0, 0],
/*C 2*/[1, 0, 0, 1, 0, 0, 1, 0, 0],
/*D 3*/[1, 0, 1, 0, 0, 0, 1, 1, 0],
/*E 4*/[0, 1, 0, 0, 0, 0, 0, 0, 1],
/*F 5*/[0, 1, 0, 0, 0, 0, 0, 0, 0],
/*G 6*/[0, 0, 1, 1, 0, 0, 0, 0, 0],
/*H 7*/[0, 0, 0, 1, 0, 0, 0, 0, 0],
/*I 8*/[0, 0, 0, 0, 1, 0, 0, 0, 0]
];
var vertices = ['A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I'];
function Graph(matrix, vertices) {
this.toString = function () {
var s = '';
for (var i = 0; i < matrix.length; i++) {
s += vertices[i] + ' -> ';
for (var j = 0; j < matrix[i].length; j++) {
if (matrix[i][j] > 0) {
s += vertices[j] + ' ';
}
}
s += '\n';
}
return s;
};
var initializeColor = function () {
var color = [];
for (var i = 0; i < vertices.length; i++) {
color[vertices[i]] = 'white';
}
return color;
};
this.bfs = function (v, callback) {
var color = initializeColor(),
queue = new Queue();
queue.enqueue(v);
while (!queue.isEmpty()) {
var u = queue.dequeue(),
neighbors = matrix[u];
color[vertices[u]] = 'grey';
for (var i = 0; i < neighbors.length; i++) {
if(neighbors[i] > 0) {
var w = vertices[i];
if (color[w] === 'white') {
color[w] = 'grey';
queue.enqueue(i);
}
}
}
color[vertices[u]] = 'black';
if (callback) {
callback(vertices[u]);
}
}
};
this.dfsStack = function (v, callback) {
var color = initializeColor(),
stack = new Stack();
stack.push(v);
while (!stack.isEmpty()) {
var u = stack.pop(),
neighbors = matrix[u];
color[vertices[u]] = 'grey';
for (var i = 0; i < neighbors.length; i++) {
if(neighbors[i] > 0) {
var w = vertices[i];
if (color[w] === 'white') {
color[w] = 'grey';
stack.push(i);
}
}
}
color[vertices[u]] = 'black';
if (callback) {
callback(vertices[u]);
}
}
};
}
function printNode(value) {
console.log('Visited vertex: ' + value);
}
var graph = new Graph(matrix, vertices);
//console.log(graph.toString());
graph.bfs(0, printNode);
graph.dfsStack(0, printNode);
</script>
</html>
输出
广度优先搜索 | 深度优先搜索 |
---|---|
A B C D E F G H I | A D H G C B F E I |
支撑图的数据结构
struct.js 栈、队列、字典
//栈
function Stack() {
let items = [];
//入栈
this.push = function (element) {
items.push(element);
};
//出栈
this.pop = function () {
return items.pop();
};
//查看栈顶元素
this.peek = function () {
return items[items.length - 1];
};
this.isEmpty = function () {
return items.length == 0;
};
this.clear = function () {
items = [];
};
this.print = function () {
console.log(items.toString());
};
}
//队列
function Queue() {
let items = [];
//入队
this.enqueue = function (element) {
items.push(element);
};
//出队
this.dequeue = function () {
return items.shift();
};
//查看队首元素
this.front = function () {
return items[0];
};
this.isEmpty = function () {
return items.length == 0;
};
this.size = function () {
return items.length;
};
this.print = function () {
console.log(items.toString());
};
}
//字典
function Dictionary() {
var items = {};
this.has = function (key) {
return key in items;
};
this.set = function (key, value) {
items[key] = value;
};
this.get = function (key) {
return this.has(key) ? items[key] : undefined;
};
this.delete = function (key) {
if (this.has(key)) {
delete items[key];
return true;
}
return false;
};
this.values = function () {
var values = [];
for (var k in items) {
if (this.has(k)) {
values.push(items[k]);
}
}
return values;
};
this.keys = function () {
return Object.keys(items);
};
this.getItems = function () {
return items;
};
}
参考文献
[1] [巴西]Loiane Groner. 学习JavaScript数据结构与算法(第2版)[M]. 人民邮电出版社,2017.
[2] [日本]石田保辉,宫崎修一. 我的第一本算法书[M]. 人民邮电出版社,2018.