广度优先搜索(BFS)
广度优先搜索(Breadth-First-Search),会从指定的第一个顶点开始遍历图,先访问这个顶点的所有相邻顶点,然后再访问这些相邻顶点的相邻顶点:
加权的图,广度优先算法并不是最合适的。
let Colors = {
WHITE: 0, // 邻接顶点没有被访问过(颜色为白色)
GREY: 1, // 被访问过(颜色为灰色)
BLACK: 2 // 开始顶点(颜色为黑色)
}
let initializeColor = vertices => {
let color = {}
vertices.forEach(v => color[v] = Colors.WHITE)
return color
}
let BFS = (graph, startVertex) => {
let vertices = graph.getVertices()
let adjList = graph.getAdjList()
let color = initializeColor(vertices) // 初始化所有的顶点标记为未被访问过(颜色为白色)
let queue = new Queue()
let distances = {} // 记录给定顶点到其它所有顶点的距离
let predecessors = {} // 记录所有顶点的前置顶点
queue.enqueue(startVertex)
// 初始化所有顶点的距离为0,前置节点为null
vertices.forEach(v => {
distances[v] = 0
predecessors[v] = null
})
while (!queue.isEmpty()) {
let current = queue.dequeue() // 队列先进先出
adjList.get(current).forEach(ele => {
if (color[ele] === Colors.WHITE) {
color[ele] = Colors.GREY
distances[ele] = distances[current] + 1
predecessors[ele] = current
queue.enqueue(ele)
}
})
color[current] = Colors.BLACK
}
return { distances, predecessors }
}
console.log(BFS(graph, 'A'))
// {
// distances: { A: 0, B: 1, C: 1, D: 1, E: 2, F: 2, G: 2, H: 2, I: 3 },
// predecessors: {
// A: null,
// B: 'A',
// C: 'A',
// D: 'A',
// E: 'B',
// F: 'B',
// G: 'C',
// H: 'D',
// I: 'E'
// }
// }
// 求出从给定顶点A开始到其它所有顶点的最短路径
let shortestPathA = BFS(graph, 'A')
let startVertex = 'A'
myVertices.forEach(v => {
let path = new Stack()
for (let v2 = v; v2 !== startVertex; v2 = shortestPathA.predecessors[v2]) {
path.push(v2)
}
path.push(startVertex)
let s = path.pop()
while (!path.isEmpty()) {
s += ` - ${path.pop()}`
}
console.log(s)
})
// A
// A - B
// A - C
// A - D
// A - B - E
// A - B - F
// A - C - G
// A - D - H
// A - B - E - I
const INF = Number.MAX_SAFE_INTEGER
const minDistance = (dist, visited) => {
let min = INF
let minIndex = -1
for (let v = 0; v < dist.length; v++) {
if (visited[v] === false && dist[v] <= min) {
min = dist[v]
minIndex = v
}
}
return minIndex
}
const dijkstra = (graph, src) => {
const dist = []
const visited = []
const { length } = graph
for (let i = 0; i < length; i++) {
dist[i] = INF
visited[i] = false
}
dist[src] = 0
for (let i = 0; i < length - 1; i++) {
const u = minDistance(dist, visited)
visited[u] = true
for (let v = 0; v < length; v++) {
if (!visited[v] && graph[u][v] !== 0 && dist[u] !== INF && dist[u] + graph[u][v] < dist[v]) {
dist[v] = dist[u] + graph[u][v]
}
}
}
return dist
}
const floydWarshall = graph => {
const dist = []
const { length } = graph
for (let i = 0; i < length; i++) {
dist[i] = []
for (let j = 0; j < length; j++) {
if (i === j) {
dist[i][j] = 0
} else if (!isFinite(graph[i][j])) {
dist[i][j] = Infinity
} else {
dist[i][j] = graph[i][j]
}
}
}
for (let k = 0; k < length; k++) {
for (let i = 0; i < length; i++) {
for (let j = 0; j < length; j++) {
if (dist[i][k] + dist[k][j] < dist[i][j]) {
dist[i][j] = dist[i][k] + dist[k][j]
}
}
}
}
return dist
}
const INF = Number.MAX_SAFE_INTEGER
const find = (i, parent) => {
while (parent[i]) {
i = parent[i] // eslint-disable-line prefer-destructuring
}
return i
}
const union = (i, j, parent) => {
if (i !== j) {
parent[j] = i
return true
}
return false
}
const initializeCost = graph => {
const cost = []
const { length } = graph
for (let i = 0; i < length; i++) {
cost[i] = []
for (let j = 0; j < length; j++) {
if (graph[i][j] === 0) {
cost[i][j] = INF
} else {
cost[i][j] = graph[i][j]
}
}
}
return cost
}
const kruskal = graph => {
const { length } = graph
const parent = []
let ne = 0
let a
let b
let u
let v
const cost = initializeCost(graph)
while (ne < length - 1) {
for (let i = 0, min = INF; i < length; i++) {
for (let j = 0; j < length; j++) {
if (cost[i][j] < min) {
min = cost[i][j]
a = u = i
b = v = j
}
}
}
u = find(u, parent)
v = find(v, parent)
if (union(u, v, parent)) {
ne++
}
cost[a][b] = cost[b][a] = INF
}
return parent
}
const INF = Number.MAX_SAFE_INTEGER
const minKey = (graph, key, visited) => {
// Initialize min value
let min = INF
let minIndex = 0
for (let v = 0; v < graph.length; v++) {
if (visited[v] === false && key[v] < min) {
min = key[v]
minIndex = v
}
}
return minIndex
}
const prim = graph => {
const parent = []
const key = []
const visited = []
const { length } = graph
for (let i = 0; i < length; i++) {
key[i] = INF
visited[i] = false
}
key[0] = 0
parent[0] = -1
for (let i = 0; i < length - 1; i++) {
const u = minKey(graph, key, visited)
visited[u] = true
for (let v = 0; v < length; v++) {
if (graph[u][v] && !visited[v] && graph[u][v] < key[v]) {
parent[v] = u
key[v] = graph[u][v]
}
}
}
return parent
}
深度优先搜索(DFS)
深度优先搜索(Depth-First-Search),从图的第一个顶点开始,沿着这个顶点的一条路径递归查找到最后一个顶点,然后返回并探查路径上的其它路径,直到所有路径都被访问到。最终,深度优先算法会先深后广地访问图中的所有顶点。
const Colors = {
WHITE: 0,
GREY: 1,
BLACK: 2
}
const initializeColor = vertices => {
let color = {}
vertices.forEach(v => color[v] = Colors.WHITE)
return color
}
const depthFirstSearch = (graph, callback) => {
let vertices = graph.getVertices()
let adjList = graph.getAdjList()
let color = initializeColor(vertices) // 初始化将图中所有顶点的颜色初始化为白色。
vertices.forEach(v => { // 挨个遍历
if (color[v] === Colors.WHITE) {
depthFirstSearchVisit(v, color, adjList, callback)
}
})
}
const depthFirstSearchVisit = (u, color, adjList, callback) => {
color[u] = Colors.GREY // 进来访问就置灰
if (callback) callback(u)
adjList.get(u).forEach(n => { // 递归遍历相邻节点
if (color[n] === Colors.WHITE) {
depthFirstSearchVisit(n, color, adjList, callback)
}
})
color[u] = Colors.BLACK // 最后没有邻接节点就设置为黑色
}
depthFirstSearch(graph, value => console.log(`visited vertex: ${value}`));
// visited vertex: A
// visited vertex: B
// visited vertex: E
// visited vertex: I
// visited vertex: F
// visited vertex: C
// visited vertex: D
// visited vertex: G
// visited vertex: H
let DFSVisit = (u, color, discovery, finished, predecessors, time, adjList) => {
color[u] = Colors.GREY
discovery[u] = ++time.count
adjList.get(u).forEach(n => {
if (color[n] === Colors.WHITE) {
predecessors[n] = u
DFSVisit(n, color, discovery, finished, predecessors, time, adjList)
}
})
color[u] = Colors.BLACK
finished[u] = ++time.count
}
let DFS = graph => {
let vertices = graph.getVertices()
let adjList = graph.getAdjList()
let color = initializeColor(vertices)
let discovery = {} // 保存每个顶点的发现时间
let finished = {} // 保存每个顶点探索时间
let predecessors = {} // 保存每个顶点的前置顶点
let time = { count: 0 } // 假定时间从0开始,每经过一步时间值加1
vertices.forEach(v => {
finished[v] = 0
discovery[v] = 0
predecessors[v] = null
})
vertices.forEach(v => {
if (color[v] === Colors.WHITE) {
DFSVisit(v, color, discovery, finished, predecessors, time, adjList)
}
})
return { discovery, finished, predecessors }
}
console.log(DFS(graph))
// {
// discovery: { A: 1, B: 2, C: 10, D: 11, E: 3, F: 7, G: 12, H: 14, I: 4 },
// finished: { A: 18, B: 9, C: 17, D: 16, E: 6, F: 8, G: 13, H: 15, I: 5 },
// predecessors: {
// A: null,
// B: 'A',
// C: 'A',
// D: 'C',
// E: 'B',
// F: 'B',
// G: 'D',
// H: 'D',
// I: 'E'
// }
// }