图的概念
图用来表示顶点与顶点间的关系
相关概念
- 顶点:事物通常用 V(Vertex)表示顶点的集合。
- 边:两个事物间的关系通常用 E(Edge)表示边的集合。
- 相邻顶点:一条边连接的顶点。
- 度:一个顶点连接的边的条数。
- 路径:路径是顶点
v1,v2…,vn的一个连续序列。 - 根据边的有无方向分为:无向图 和 有向图。
图的表示
顶点表示
顶点的表示抽象成数字或者字母表示。
边的表示
- 邻接矩阵:两个顶点间的边用1表示,无边连接的两个顶点间用0表示。当为带权图时,可以为权重。
- 当图为稀疏图时(顶点很多,边较少)。要用大量的0 表示两个顶点之间无边连接,已造成空间浪费。
字典封装
function Dictionary() {
// 字典属性
this.items = {};
// 操作方法
// 添加key和value
Dictionary.prototype.set = function (key, value) {
this.items[key] = value;
};
// 判断字典中是否有这个key hasOwnProperty检测一个属性是否是对象的自有属性
Dictionary.prototype.has = function (key) {
return this.items.hasOwnProperty(key);
};
// 从字典中删除元素
Dictionary.prototype.remove = function (key) {
// 检查有没有这个元素
if (!this.has(key)) {
return false;
}
delete this.items[key];
return true;
};
//根据key找value
Dictionary.prototype.get = function (key) {
return this.has(key) ? this.items[key] : undefined;
};
// 获取所有的key
Dictionary.prototype.keys = function () {
return Object.keys(this.items);
};
// 获取所有的value
Dictionary.prototype.values = function () {
return Object.values(this.items);
};
// size()
Dictionary.prototype.size = function () {
return Object.keys(this.items).length;
};
// clear()
Dictionary.prototype.clear = function () {
this.items = {};
};
}
图封装
function Graph() {
// 属性: 顶点(数组) / 边(字典)
this.vertexes = []; // 顶点
this.edges = new Dictionary(); // 边
}
添加顶点
Graph.prototype.addVertex = function (v) {
this.vertexes.push(v);
this.edges.set(v, []);
};
添加边
Graph.prototype.addEdge = function (v1, v2) {
this.edges.get(v1).push(v2); // 取出v1对应的数组 帮把v2加进去 v1->v2有边
this.edges.get(v2).push(v1); // v2->v1也有边
};
toString()
Graph.prototype.toString = function () {
let res = "";
// 遍历顶点
for (let i = 0; i < this.vertexes.length; i++) {
res += this.vertexes[i] + "->";
// 取出顶点相对应的数组 ,并遍历
let itemsEdges = this.edges.get(this.vertexes[i]);
for (let j = 0; j < itemsEdges.length; j++) {
res += itemsEdges[j] + " ";
}
res += "\n";
}
return res;
};
/*
记录顶点是否被访问过,三种颜色表示
白色 该顶点还没有被访问过
灰色 该顶点被访问过但是未被探索
黑色 该顶点欸访问过且被探索过
*/
初始化颜色 initColor()
Graph.prototype.initColor = function () {
let colors = [];
for (let i = 0; i < this.vertexes.length; i++) {
colors[this.vertexes[i]] = "white";
}
return colors;
};
BFS和DFS
BFS 基于队列 ,入队列的顶点先被探索
DFS 基于栈或使用递归,通过将顶点存入栈中,顶点是沿着路径被探索的,存在新的相邻顶点就去访问
广度优先搜索
/*
广度优先算法会从指定的第一个顶点开始遍历图,先访问其所有的相邻,即先宽后深的访问顶点
广度优先的搜索实现
1 创建一个队列Q
2 将v标注为被发现的(灰色),并将v加入队列Q
3 如果Q是非空,执行下面的步骤
3.1 将v从Q中取出队列
3.2 将v标注为被发现的灰色
3.3 将v所有的未被访问过的邻接点(白色),加入队列中
3.4 将v标志为黑色
Graph.prototype.bfs = function (initV, handler) {
// 初始化颜色
let colors = this.initColor();
// 创建队列
let list = [];
// 加入队列
list.push(initV);
// 循环从队列中取出元素
while (list.length) {
// 从队列取出一个顶点
let v = list.shift();
// 获取顶点相邻的另外节点
let vList = this.edges.get(v); // 加入顶点为A 则相邻为B C D
// 将v的颜色设置成灰色
colors[v] = "gray";
// 遍历v的相邻顶点
for (let i = 0; i < vList.length; i++) {
let e = vList[i];
// 如图,存在C和D的相邻顶点G, 顶点G会被多次访问到 ,所以只有白色才添加
if (colors[e] == "white") {
colors[e] = "gray";
list.push(e);
}
}
// 访问顶点
handler(v);
// 将顶点改为黑色
colors[v] = "black";
}
};
深度优先搜索
/*
深度优先搜索 如图 图-深度优先搜索
深度优先搜索算法将会从第一个指定的顶点开始遍历图,沿着路径知道这条路径最后被访问了
接着原路退回并探索下一条路径
深度优先搜索算法的实现
递归,函数栈的调用
*/
Graph.prototype.dfs = function (initV, handler) {
// 初始化颜色
let colors = this.initColor();
// 从某个顶点开始依次递归开始访问
this.dfsVisited(initV, colors, handler);
};
Graph.prototype.dfsVisited = function (v, colors, handler) {
// 将颜色设置为灰色
colors[v] = "gray";
// 处理顶点v
handler(v);
// 访问v的邻接点
let vList = this.edges.get(v);
for (let i = 0; i < vList.length; i++) {
if (colors[vList[i]] == "white") {
this.dfsVisited(vList[i], colors, handler);
}
}
colors[v] = "black";
};
测试
let graph = new Graph();
// ============================================================
// 添加顶点
let newVertexes = ["A", "B", "C", "D", "E", "F", "G", "H", "I"];
newVertexes.forEach((v) => {
graph.addVertex(v);
});
// 添加边
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");
let res = graph.toString();
console.log(res);
// res
/*
A->B C D
B->A E F
C->A D G
D->A C G H
E->B I
F->B
G->C D
H->D
I->E
*/
// ====================================================
// 广度优先搜索
let result = "";
graph.bfs(graph.vertexes[0], function (v) {
result += v + " ";
});
console.log("result", result); // A B C D E F G H I
// =====================================================
// 深度优先搜索
let result2 = "";
graph.dfs(graph.vertexes[0], function (v) {
result2 += v + " ";
});
console.log("result2", result2); // A B E I F C D G H