图
图是一种复杂的网状结构,由顶点(Vertex)和边(Edge)组成,用于表示对象之间的关系。图在计算机科学中有着广泛的应用,如社交网络、路由算法、任务调度等。
图的基本概念
- 顶点(Vertex):图中的基本单位,也称为节点(Node)
- 边(Edge):连接两个顶点的线,表示顶点之间的关系
- 度(Degree):与顶点相连的边的数量
- 路径(Path):从一个顶点到另一个顶点的边的序列
- 环(Cycle):起点和终点相同的路径
- 连通性(Connectivity):图中任意两个顶点之间是否存在路径
图的种类
图可以分为以下几种:
- 有向图:图中边有方向,表示从一个顶点到另一个顶点的关系。
- 无向图:图中边没有方向,表示两个顶点之间的关系。
- 加权图:图中边有权值,表示两个顶点之间的关系的强度或距离。
- 无权图:图中边没有权值,表示两个顶点之间的关系。
图的表示方法
邻接矩阵
使用二维数组表示图中顶点之间的连接关系。
js
class Graph {
constructor(vertices) {
this.vertices = vertices;
this.matrix = Array(vertices).fill().map(() => Array(vertices).fill(0));
}
addEdge(v1, v2, weight = 1) {
this.matrix[v1][v2] = weight;
this.matrix[v2][v1] = weight; // 无向图需要双向设置
}
removeEdge(v1, v2) {
this.matrix[v1][v2] = 0;
this.matrix[v2][v1] = 0;
}
hasEdge(v1, v2) {
return this.matrix[v1][v2] !== 0;
}
}
邻接表
使用数组和链表表示图中顶点之间的连接关系。
js
class Graph {
constructor(vertices) {
this.vertices = vertices;
this.adjList = new Map();
for (let i = 0; i < vertices; i++) {
this.adjList.set(i, []);
}
}
addEdge(v1, v2, weight = 1) {
this.adjList.get(v1).push({ vertex: v2, weight });
this.adjList.get(v2).push({ vertex: v1, weight }); // 无向图需要双向设置
}
removeEdge(v1, v2) {
this.adjList.set(v1, this.adjList.get(v1).filter(edge => edge.vertex !== v2));
this.adjList.set(v2, this.adjList.get(v2).filter(edge => edge.vertex !== v1));
}
hasEdge(v1, v2) {
return this.adjList.get(v1).some(edge => edge.vertex === v2);
}
}
图的遍历
图的遍历是指按照某种规则访问图中所有顶点的过程。图的遍历有以下两种:
深度优先遍历
深度优先遍历(Depth-First Search,DFS)是指从一个顶点开始,沿着图中某条边向下深入到图的最深处,然后回溯到上一个顶点。这种遍历方式使用栈数据结构来实现。
js
class Graph {
// ... 前面的代码保持不变 ...
dfs(startVertex) {
const visited = new Set();
const result = [];
const dfsHelper = (vertex) => {
visited.add(vertex);
result.push(vertex);
const neighbors = this.adjList.get(vertex);
for (const neighbor of neighbors) {
if (!visited.has(neighbor.vertex)) {
dfsHelper(neighbor.vertex);
}
}
};
dfsHelper(startVertex);
return result;
}
}
广度优先遍历
广度优先遍历(Breadth-First Search,BFS)是指从一个顶点开始,访问所有与其直接相连的顶点,然后访问与这些顶点直接相连的顶点,依此类推。这种遍历方式使用队列数据结构来实现。
js
class Graph {
// ... 前面的代码保持不变 ...
bfs(startVertex) {
const visited = new Set();
const queue = [startVertex];
const result = [];
visited.add(startVertex);
while (queue.length > 0) {
const vertex = queue.shift();
result.push(vertex);
const neighbors = this.adjList.get(vertex);
for (const neighbor of neighbors) {
if (!visited.has(neighbor.vertex)) {
visited.add(neighbor.vertex);
queue.push(neighbor.vertex);
}
}
}
return result;
}
}
图的最短路径
Dijkstra算法
用于计算单源最短路径,适用于没有负权边的图。
js
class Graph {
// ... 前面的代码保持不变 ...
dijkstra(startVertex) {
const distances = new Map();
const visited = new Set();
const previous = new Map();
// 初始化距离
for (let i = 0; i < this.vertices; i++) {
distances.set(i, Infinity);
}
distances.set(startVertex, 0);
while (visited.size < this.vertices) {
// 找到未访问的最小距离顶点
let minVertex = null;
let minDistance = Infinity;
for (let i = 0; i < this.vertices; i++) {
if (!visited.has(i) && distances.get(i) < minDistance) {
minVertex = i;
minDistance = distances.get(i);
}
}
if (minVertex === null) break;
visited.add(minVertex);
// 更新相邻顶点的距离
const neighbors = this.adjList.get(minVertex);
for (const neighbor of neighbors) {
const newDistance = distances.get(minVertex) + neighbor.weight;
if (newDistance < distances.get(neighbor.vertex)) {
distances.set(neighbor.vertex, newDistance);
previous.set(neighbor.vertex, minVertex);
}
}
}
return { distances, previous };
}
}
Floyd-Warshall算法
用于计算所有顶点之间的最短路径。
js
class Graph {
// ... 前面的代码保持不变 ...
floydWarshall() {
const dist = Array(this.vertices).fill().map(() => Array(this.vertices).fill(Infinity));
// 初始化距离矩阵
for (let i = 0; i < this.vertices; i++) {
dist[i][i] = 0;
const neighbors = this.adjList.get(i);
for (const neighbor of neighbors) {
dist[i][neighbor.vertex] = neighbor.weight;
}
}
// 动态规划计算最短路径
for (let k = 0; k < this.vertices; k++) {
for (let i = 0; i < this.vertices; i++) {
for (let j = 0; j < this.vertices; j++) {
if (dist[i][k] + dist[k][j] < dist[i][j]) {
dist[i][j] = dist[i][k] + dist[k][j];
}
}
}
}
return dist;
}
}
最小生成树
Kruskal算法
用于寻找无向图的最小生成树。
js
class Graph {
// ... 前面的代码保持不变 ...
kruskal() {
const edges = [];
const result = [];
const parent = Array(this.vertices).fill().map((_, i) => i);
// 收集所有边
for (let i = 0; i < this.vertices; i++) {
const neighbors = this.adjList.get(i);
for (const neighbor of neighbors) {
if (neighbor.vertex > i) { // 避免重复添加边
edges.push({
from: i,
to: neighbor.vertex,
weight: neighbor.weight
});
}
}
}
// 按权重排序
edges.sort((a, b) => a.weight - b.weight);
// 查找根节点
const find = (x) => {
if (parent[x] !== x) {
parent[x] = find(parent[x]);
}
return parent[x];
};
// 合并集合
const union = (x, y) => {
const rootX = find(x);
const rootY = find(y);
if (rootX !== rootY) {
parent[rootY] = rootX;
return true;
}
return false;
};
// 构建最小生成树
for (const edge of edges) {
if (union(edge.from, edge.to)) {
result.push(edge);
}
}
return result;
}
}
图的应用场景
- 社交网络:用户之间的关系可以用图表示
- 路由算法:网络中的路由选择
- 任务调度:任务之间的依赖关系
- 推荐系统:用户和物品之间的关系
- 地图导航:地点之间的路径规划
- 电路设计:电子元件之间的连接关系
- 编译器优化:程序中的控制流图
- 生物信息学:蛋白质相互作用网络