图(网路)
图由边的集合及顶点的集合组成。利用图可以对现实中很多系统建模,如交通系统,飞行系统,计算机网络。
图的表示
我们可以使用顶点及到顶点的关系来表示图
0 -> 1
0 -> 2
1 -> 3
2 -> 4
构建图类
class Graph {
constructor(v) {
this.vertices = v;
// 边数组
this.adj = [];
this.edges = 0;
for (let i = 0; i < this.vertices; i++) {
this.adj[i] = [];
this.adj[i].push();
}
}
// 添加边
addEdge(v, w) {
this.adj[v].push(w);
this.adj[w].push(v);
this.edges++;
}
// 显示图
showGraph() {
for (let i = 0; i < this.vertices; i++) {
const pre = i + ' > '
let tail = '';
for (let j = 0; j < this.vertices; j++) {
if (this.adj[i].indexOf(j) > -1) {
tail += j + ' '
}
}
console.log(pre + tail);
}
}
}
搜索图
搜索分为深度优先搜索和广度优先搜索
深度优先搜索
// 数组保存已搜索节点
this.visited = [];
// ···
dfs(v) {
const adjv = this.adj[v];
this.visited[v] = true;
console.log(v);
if (adjv) {
for (let i = 0, len = adjv.length; i < len; i++) {
const w = adjv[i];
if (!this.visited[w]) {
this.dfs(w);
}
}
}
}
广度优先搜索
// ···
bfs(v) {
const queue = [v];
this.visited[v] = true;
// 利用队列来实现遍历,非递归
while(queue.length) {
const v = queue.shift();
const adjv = this.adj[v];
console.log(v);
if (adjv) {
for (let i = 0, len = adjv.length; i < len; i++) {
const w = adjv[i];
if (!this.visited[w]) {
queue.push(w)
this.visited[w] = true;
}
}
}
}
}
确定最短路径(无权重)
现实生活中,我们求 A->B 的最短路径(站点最少)时,我们一般会先找 A->B 单边路径,再找双边路径,三边路径等。其实这就是我们图的广度优先搜索,得到B所在层数,再串联路径则是 A->B 的最短路径。
// 广度优先遍历时记录上层节点
this.edgeTo = [];
// ···
bfs(v) {
const queue = [v];
this.visited[v] = true;
// 利用队列来实现遍历,非递归
while(queue.length) {
const v = queue.shift();
const adjv = this.adj[v];
console.log(v);
if (adjv) {
for (let i = 0, len = adjv.length; i < len; i++) {
const w = adjv[i];
if (!this.visited[w]) {
this.edgeTo[w] = v;
queue.push(w)
this.visited[w] = true;
}
}
}
}
}
pathTo(source, destination) {
this.visited = [];
g.bfs(source);
if (!this.hasPathTo(destination)) {
return null;
}
const path = [destination];
while(this.edgeTo[destination] !== source) {
path.push(this.edgeTo[destination]);
destination = this.edgeTo[destination];
}
path.push(source);
path.reverse()
console.log(path.toString());
return path;
}
hasPathTo(v) {
return this.visited[v];
}
完整代码
class Graph {
constructor(v) {
this.vertices = v;
this.edges = 0;
this.visited = [];
this.adj = [];
this.edgeTo = [];
for (let i = 0; i < this.vertices; i++) {
this.adj[i] = [];
this.adj[i].push();
}
}
addEdge(v, w) {
this.adj[v].push(w);
this.adj[w].push(v);
this.edges++;
}
showGraph() {
for (let i = 0; i < this.vertices; i++) {
const pre = i + ' > '
let tail = '';
for (let j = 0; j < this.vertices; j++) {
if (this.adj[i].indexOf(j) > -1) {
tail += j + ' '
}
}
console.log(pre + tail);
}
}
dfs(v) {
this.visited[v] = true;
console.log(v);
if (this.adj[v]) {
for (let i = 0, len = this.adj[v].length; i < len; i++) {
const w = this.adj[v][i];
if (!this.visited[w]) {
this.dfs(w);
}
}
}
}
bfs(v) {
const queue = [v];
this.visited[v] = true;
while(queue.length) {
const v = queue.shift();
console.log(v);
for (let i = 0, len = this.adj[v].length; i < len; i++) {
const w = this.adj[v][i];
if (!this.visited[w]) {
this.edgeTo[w] = v;
queue.push(w)
this.visited[w] = true;
}
}
}
}
pathTo(source, destination) {
this.visited = [];
g.bfs(source);
if (!this.hasPathTo(destination)) {
return null;
}
const path = [destination];
while(this.edgeTo[destination] !== source) {
path.push(this.edgeTo[destination]);
destination = this.edgeTo[destination];
}
path.push(source);
path.reverse()
console.log(path.toString());
return path;
}
hasPathTo(v) {
return this.visited[v];
}
}
欢迎到前端学习打卡群一起学习~516913974