huan_kong
900字约3分钟
2024-05-08
一组顶点:
V
(Vertex
) 表示顶点的集合一组边:通常用 E
(Edge
) 表示边的集合
顶点
边
相邻顶点
0 - 1
是相邻的, 0 - 3
是相邻的. 0 - 2
是不相邻的.度
0
顶点和其他两个顶点相连, 0
顶点的度是 2
1
顶点和其他四个顶点相连, 1
顶点的度是 4
路径
v1
, v2
..., vn
的一个连续序列, 比如上图中 0 1 5 9
就是一条路径.0 1 5 9
是一条简单路径.0 1 5 6 3 0
.无向图
0 - 1
之间有边, 那么说明这条边可以保证 0 -> 1
, 也可以保证 1 -> 0
.有向图
0 -> 1
, 不能保证一定可以 1 -> 0
, 要根据方向来定.无权图
0 - 1
的边, 比 4 - 9
的边更远或者用的时间更长.带权图
我们可以制定一个规则, 0表示他们没有链接, 1表示他们有链接
如果这里需要权重, 那么0的意思不变, 后面别的数字就可以是他们的权重
不过这种方法有些浪费空间, 存在大量0
类似哈希表的存储方式
获取链接到了谁很容易, 但是如果想需要获取谁链接了自己很难, 可以使用逆邻接表
都需要指明第一个被访问的顶点
把当前节点的所有分支全部走一遍, 然后再重新到分支的节点, 把他的所有分支都再走一遍
一条路走到底, 走到底然后开始往回看, 检查前面节点的别的分支是否都已经经过
namespace 图 {
class Dictionay<T> {
#values: { [key: string]: T } = {}
has(key: string) {
return this.#values.hasOwnProperty(key)
}
set(key: string, value: T) {
this.#values[key] = value
}
remove(key: string) {
if (!this.has(key)) return false
delete this.#values[key]
}
get(key: string) {
return this.has(key) ? this.#values[key] : undefined
}
keys() {
return Object.keys(this.#values)
}
values() {
return Object.values(this.#values)
}
size() {
return this.keys().length
}
clear() {
this.#values = {}
}
}
class Queue<T> {
#value: T[] = []
enqueue(element: T) {
return this.#value.push(element)
}
dequeue() {
return this.#value.shift()
}
front() {
return this.#value[0]
}
isEmpty() {
return this.#value.length === 0
}
size() {
return this.#value.length
}
toString() {
return this.#value.toString()
}
}
interface color {
[key: string]: 'white' | 'gray' | 'black'
}
type func = (value: string) => void
class Graph {
// 顶点
#vertexes: string[] = []
#edges: Dictionay<string[]> = new Dictionay()
constructor() {}
addVertex(vertex: string) {
if (this.#vertexes.includes(vertex)) return
this.#vertexes.push(vertex)
this.#edges.set(vertex, [])
}
addEdge(vertex1: string, vertex2: string) {
if (!this.#edges.has(vertex1) || !this.#edges.has(vertex2)) return
this.#edges.get(vertex1)!.push(vertex2)
this.#edges.get(vertex2)!.push(vertex1)
}
toString() {
return this.#vertexes
.map((vertex) => `${vertex} => ${this.#edges.get(vertex)?.join(' ')}`)
.join('\n')
}
initColor(): color {
/**
* @description 白色: 没有被访问过
* @description 灰色: 被访问过, 但没有完成探索
* @description 黑色: 被访问过, 并且探索完成
*/
return this.#vertexes.reduce((previousValue, currentValue) => {
previousValue[currentValue] = 'white'
return previousValue
}, {})
}
// 广度优先搜索算法
BFS(initVertexes: string, func: func) {
const queue = new Queue<string>()
const color = this.initColor()
queue.enqueue(initVertexes)
while (!queue.isEmpty()) {
// 取出顶点
const currentVertex = queue.dequeue() as string
// 获取相连的其余顶点
const edges = this.#edges.get(currentVertex) as string[]
// 设置为灰色
color[initVertexes] = 'gray'
// 遍历所有边, 添加到队列中
edges.forEach((edge) => {
// 如果是白色, 则设置为灰色, 并添加到队列中
// 如果是灰色, 则跳过, 因为已经被访问过了
if (color[edge] === 'white') {
color[edge] = 'gray'
queue.enqueue(edge)
}
})
func(currentVertex)
color[initVertexes] = 'black'
}
}
// 深度优先搜索算法(队列方法)
DFSQueue(initVertexes: string, func: func) {
const queue = new Queue<string>()
const color = this.initColor()
queue.enqueue(initVertexes)
while (!queue.isEmpty()) {
// 取出顶点
const currentVertex = queue.dequeue() as string
// 获取相连的其余顶点
const edges = this.#edges.get(currentVertex) as string[]
// 设置为灰色
color[initVertexes] = 'gray'
// 遍历所有边, 添加到队列中
edges.forEach((edge) => {
// 如果是白色, 则设置为灰色, 并添加到队列中
if (color[edge] === 'white') {
color[edge] = 'gray'
queue.enqueue(edge)
}
})
func(currentVertex)
color[initVertexes] = 'black'
}
}
// 深度优先搜索算法(递归方法)
DFS(initVertexes: string, func: func) {
const color = this.initColor()
this.DFSVisit(initVertexes, color, func)
}
DFSVisit(vertexes: string, color: color, func: func) {
color[vertexes] = 'gray'
// 获取相连的其余顶点
const edges = this.#edges.get(vertexes) as string[]
// 设置为灰色
color[vertexes] = 'gray'
func(vertexes)
// 遍历所有边
edges.forEach((edge) => {
// 如果是白色, 则设置为灰色, 并开始递归调用
if (color[edge] === 'white') {
color[edge] = 'gray'
this.DFSVisit(edge, color, func)
}
})
color[vertexes] = 'black'
}
}
}