图的基础知识
图 (Graph) 是由顶点 (Vertex) 和边 (Edge) 组成的数据结构,用于表示对象之间的关系。
图的分类
图的存储方式
| 存储方式 | 空间复杂度 | 查询边 | 遍历邻居 | 适用场景 |
|---|---|---|---|---|
| 邻接矩阵 | O(V²) | O(1) | O(V) | 稠密图 |
| 邻接表 | O(V+E) | O(degree) | O(degree) | 稀疏图 |
| 边列表 | O(E) | O(E) | O(E) | Kruskal 等算法 |
Dijkstra 算法 - 单源最短路径
Dijkstra 算法用于在带权图中找到从起点到所有其他节点的最短路径,适用于权值为非负数的情况。
Dijkstra 算法可视化
🎯 Dijkstra
单源最短路径,贪心策略
适用于非负权图,使用优先队列优化
🔄 Bellman-Ford
单源最短路径,可处理负权
可检测负权环,适用于有负权边的图
📊 Floyd-Warshall
多源最短路径,动态规划
计算所有点对之间的最短路径
// Dijkstra 算法实现
function dijkstra(graph, start) {
const n = graph.length;
const dist = Array(n).fill(Infinity);
const visited = Array(n).fill(false);
const prev = Array(n).fill(-1);
dist[start] = 0;
for (let i = 0; i < n; i++) {
// 找到未访问的最小距离节点
let u = -1;
for (let v = 0; v < n; v++) {
if (!visited[v] && (u === -1 || dist[v] < dist[u])) {
u = v;
}
}
if (dist[u] === Infinity) break;
visited[u] = true;
// 更新邻居节点
for (const [v, weight] of graph[u]) {
if (!visited[v] && dist[u] + weight < dist[v]) {
dist[v] = dist[u] + weight;
prev[v] = u;
}
}
}
return { dist, prev };
}
// 使用优先队列优化的版本
function dijkstraPQ(graph, start) {
const n = graph.length;
const dist = Array(n).fill(Infinity);
const prev = Array(n).fill(-1);
const pq = new PriorityQueue();
dist[start] = 0;
pq.push(start, 0);
while (!pq.isEmpty()) {
const u = pq.pop();
for (const [v, weight] of graph[u]) {
if (dist[u] + weight < dist[v]) {
dist[v] = dist[u] + weight;
prev[v] = u;
pq.push(v, dist[v]);
}
}
}
return { dist, prev };
}
// 简单优先队列实现
class PriorityQueue {
constructor() {
this.items = [];
}
push(element, priority) {
this.items.push({ element, priority });
this.items.sort((a, b) => a.priority - b.priority);
}
pop() {
return this.items.shift().element;
}
isEmpty() {
return this.items.length === 0;
}
}
// 测试
const graph = [
[[1, 4], [2, 2]], // 节点 0
[[0, 4], [2, 5], [3, 10]], // 节点 1
[[0, 2], [1, 5], [3, 3]], // 节点 2
[[1, 10], [2, 3]] // 节点 3
];
console.log(dijkstra(graph, 0));
// dist: [0, 4, 2, 5]import heapq
def dijkstra(graph, start):
"""
Dijkstra 算法 - 优先队列优化版本
graph: 邻接表,graph[u] = [(v, weight), ...]
"""
n = len(graph)
dist = [float('inf')] * n
prev = [-1] * n
dist[start] = 0
# 优先队列:(distance, node)
pq = [(0, start)]
while pq:
d, u = heapq.heappop(pq)
if d > dist[u]:
continue
for v, weight in graph[u]:
new_dist = dist[u] + weight
if new_dist < dist[v]:
dist[v] = new_dist
prev[v] = u
heapq.heappush(pq, (new_dist, v))
return dist, prev
# 测试
graph = [
[(1, 4), (2, 2)], # 节点 0
[(0, 4), (2, 5), (3, 10)], # 节点 1
[(0, 2), (1, 5), (3, 3)], # 节点 2
[(1, 10), (2, 3)] # 节点 3
]
dist, prev = dijkstra(graph, 0)
print(f"最短距离:{dist}") # [0, 4, 2, 5]
print(f"前驱节点:{prev}") # [-1, 0, 0, 2]package main
import (
"container/heap"
"fmt"
)
type Item struct {
node int
distance int
index int
}
type PriorityQueue []*Item
func (pq PriorityQueue) Len() int { return len(pq) }
func (pq PriorityQueue) Less(i, j int) bool {
return pq[i].distance < pq[j].distance
}
func (pq PriorityQueue) Swap(i, j int) {
pq[i], pq[j] = pq[j], pq[i]
pq[i].index = i
pq[j].index = j
}
func (pq *PriorityQueue) Push(x interface{}) {
item := x.(*Item)
item.index = len(*pq)
*pq = append(*pq, item)
}
func (pq *PriorityQueue) Pop() interface{} {
old := *pq
n := len(old)
item := old[n-1]
*pq = old[0 : n-1]
return item
}
func dijkstra(graph [][][2]int, start int) []int {
n := len(graph)
dist := make([]int, n)
for i := range dist {
dist[i] = 1e9
}
dist[start] = 0
pq := &PriorityQueue{}
heap.Init(pq)
heap.Push(pq, &Item{node: start, distance: 0})
for pq.Len() > 0 {
item := heap.Pop(pq).(*Item)
u := item.node
d := item.distance
if d > dist[u] {
continue
}
for _, edge := range graph[u] {
v, weight := edge[0], edge[1]
if dist[u]+weight < dist[v] {
dist[v] = dist[u] + weight
heap.Push(pq, &Item{node: v, distance: dist[v]})
}
}
}
return dist
}
func main() {
graph := [][][2]int{
{{1, 4}, {2, 2}},
{{0, 4}, {2, 5}, {3, 10}},
{{0, 2}, {1, 5}, {3, 3}},
{{1, 10}, {2, 3}},
}
fmt.Println(dijkstra(graph, 0)) // [0 4 2 5]
}Floyd-Warshall 算法 - 多源最短路径
Floyd-Warshall 算法用于计算图中所有节点对之间的最短路径,采用动态规划思想。
function floydWarshall(graph) {
const n = graph.length;
const dist = Array(n).fill(null).map(() => Array(n).fill(Infinity));
// 初始化
for (let i = 0; i < n; i++) {
dist[i][i] = 0;
}
// 填充直接边权重
for (let u = 0; u < n; u++) {
for (const [v, w] of graph[u]) {
dist[u][v] = w;
}
}
// Floyd-Warshall 核心
for (let k = 0; k < n; k++) {
for (let i = 0; i < n; i++) {
for (let j = 0; j < n; j++) {
if (dist[i][k] + dist[k][j] < dist[i][j]) {
dist[i][j] = dist[i][k] + dist[k][j];
}
}
}
}
return dist;
}
// 测试
const graph = [
[[1, 3], [3, 7]],
[[2, 2]],
[[3, 1]],
[[0, 2]]
];
console.log(floydWarshall(graph));def floyd_warshall(graph):
"""
Floyd-Warshall 算法
graph: 邻接矩阵,graph[i][j] 表示 i 到 j 的距离
"""
n = len(graph)
dist = [row[:] for row in graph] # 深拷贝
# 对角线为 0
for i in range(n):
dist[i][i] = 0
# 核心算法
for k in range(n):
for i in range(n):
for j in range(n):
if dist[i][k] + dist[k][j] < dist[i][j]:
dist[i][j] = dist[i][k] + dist[k][j]
return dist
# 测试
INF = float('inf')
graph = [
[0, 3, INF, 7],
[INF, 0, 2, INF],
[INF, INF, 0, 1],
[2, INF, INF, 0]
]
result = floyd_warshall(graph)
for row in result:
print(row) package main
import (
"fmt"
"math"
)
const INF = math.MaxInt32
func floydWarshall(graph [][]int) [][]int {
n := len(graph)
dist := make([][]int, n)
for i := range dist {
dist[i] = make([]int, n)
copy(dist[i], graph[i])
}
// Floyd-Warshall 核心
for k := 0; k < n; k++ {
for i := 0; i < n; i++ {
for j := 0; j < n; j++ {
if dist[i][k] != INF && dist[k][j] != INF {
if dist[i][k]+dist[k][j] < dist[i][j] {
dist[i][j] = dist[i][k] + dist[k][j]
}
}
}
}
}
return dist
}
func main() {
graph := [][]int{
{0, 3, INF, 7},
{INF, 0, 2, INF},
{INF, INF, 0, 1},
{2, INF, INF, 0},
}
result := floydWarshall(graph)
for _, row := range result {
for _, val := range row {
if val == INF {
fmt.Print("INF ")
} else {
fmt.Printf("%d ", val)
}
}
fmt.Println()
}
}