1. 图的基本概念
图是一种非线性数据结构,由顶点(Vertex)的集合和连接这些顶点的边(Edge)的集合组成。formally表示为G = (V, E),其中V是顶点集合,E是边集合。
图可以用来表示很多现实世界中的关系,比如社交网络、地图路线、网络拓扑等。
1.1 基本术语
- 顶点(Vertex):图中的基本单元,也称为节点。
- 边(Edge):连接两个顶点的线段。
- 度(Degree):与某个顶点相连的边的数量。
- 路径(Path):从一个顶点到另一个顶点的顶点序列。
- 环(Cycle):起点和终点相同的路径。
2. 图的类型
2.1 有向图与无向图
- 无向图:边没有方向,$(v,w)$和$(w,v)$表示同一条边。
- 有向图:边有方向,$(v,w)$和$(w,v)$是不同的边。
2.2 加权图与非加权图
- 非加权图:边没有与之关联的权值。
- 加权图:每条边都有一个权值,表示某种成本或距离。
2.3 其他类型
- 完全图:每个顶点都与其他所有顶点相连。
- 连通图:任意两个顶点之间都存在路径。
- 二分图:顶点可以分为两个不相交的集合,每条边连接的两个顶点分别属于这两个集合。
3. 图的表示方法
3.1 邻接矩阵
使用一个二维数组来表示图,如果顶点i和j之间有边,则matrix[i][j] = 1(或权值),否则为0。
#define MAX_VERTICES 100
int adjMatrix[MAX_VERTICES][MAX_VERTICES];
// 初始化图
void initGraph(int vertices) {
for (int i = 0; i < vertices; i++)
for (int j = 0; j < vertices; j++)
adjMatrix[i][j] = 0;
}
// 添加边
void addEdge(int start, int end) {
adjMatrix[start][end] = 1;
adjMatrix[end][start] = 1; // 对于无向图
}
// 打印图
void printGraph(int vertices) {
for (int i = 0; i < vertices; i++) {
for (int j = 0; j < vertices; j++)
printf("%d ", adjMatrix[i][j]);
printf("\n");
}
}
int main() {
int V = 5;
initGraph(V);
addEdge(0, 1);
addEdge(0, 4);
addEdge(1, 2);
addEdge(1, 3);
addEdge(1, 4);
addEdge(2, 3);
addEdge(3, 4);
printf("邻接矩阵表示:\n");
printGraph(V);
return 0;
}
输出:
邻接矩阵表示:
0 1 0 0 1
1 0 1 1 1
0 1 0 1 0
0 1 1 0 1
1 1 0 1 0
3.2 邻接表
对每个顶点,维护一个链表,存储与其相邻的顶点。
#include <stdio.h>
#include <stdlib.h>
struct Node {
int vertex;
struct Node* next;
};
struct Graph {
int numVertices;
struct Node** adjLists;
};
struct Node* createNode(int v) {
struct Node* newNode = malloc(sizeof(struct Node));
newNode->vertex = v;
newNode->next = NULL;
return newNode;
}
struct Graph* createGraph(int vertices) {
struct Graph* graph = malloc(sizeof(struct Graph));
graph->numVertices = vertices;
graph->adjLists = malloc(vertices * sizeof(struct Node*));
for (int i = 0; i < vertices; i++)
graph->adjLists[i] = NULL;
return graph;
}
void addEdge(struct Graph* graph, int src, int dest) {
struct Node* newNode = createNode(dest);
newNode->next = graph->adjLists[src];
graph->adjLists[src] = newNode;
newNode = createNode(src);
newNode->next = graph->adjLists[dest];
graph->adjLists[dest] = newNode;
}
void printGraph(struct Graph* graph) {
for (int v = 0; v < graph->numVertices; v++) {
struct Node* temp = graph->adjLists[v];
printf("\n顶点 %d 的邻接表:\n ", v);
while (temp) {
printf("%d -> ", temp->vertex);
temp = temp->next;
}
printf("\n");
}
}
int main() {
struct Graph* graph = createGraph(5);
addEdge(graph, 0, 1);
addEdge(graph, 0, 4);
addEdge(graph, 1, 2);
addEdge(graph, 1, 3);
addEdge(graph, 1, 4);
addEdge(graph, 2, 3);
addEdge(graph, 3, 4);
printGraph(graph);
return 0;
}
输出:
顶点 0 的邻接表:
4 -> 1 ->
顶点 1 的邻接表:
4 -> 3 -> 2 -> 0 ->
顶点 2 的邻接表:
3 -> 1 ->
顶点 3 的邻接表:
4 -> 2 -> 1 ->
顶点 4 的邻接表:
3 -> 1 -> 0 ->
4. 图的基本操作
4.1 深度优先搜索(DFS)
DFS从一个顶点开始,尽可能深地探索一条路径,然后回溯。
#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
#define MAX_VERTICES 100
bool visited[MAX_VERTICES];
int adjMatrix[MAX_VERTICES][MAX_VERTICES];
void dfs(int v, int vertices) {
visited[v] = true;
printf("%d ", v);
for (int i = 0; i < vertices; i++) {
if (adjMatrix[v][i] == 1 && !visited[i]) {
dfs(i, vertices);
}
}
}
int main() {
int V = 5;
// ... 初始化图和添加边的代码 ...
printf("DFS遍历结果: ");
dfs(0, V);
return 0;
}
4.2 广度优先搜索(BFS)
BFS从一个顶点开始,先访问所有邻接顶点,然后再访问下一层顶点。
#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
#define MAX_VERTICES 100
bool visited[MAX_VERTICES];
int adjMatrix[MAX_VERTICES][MAX_VERTICES];
int queue[MAX_VERTICES];
int front = -1, rear = -1;
void enqueue(int v) {
if (rear == MAX_VERTICES - 1) return;
if (front == -1) front = 0;
queue[++rear] = v;
}
int dequeue() {
if (front == -1) return -1;
int item = queue[front];
front++;
if (front > rear) front = rear = -1;
return item;
}
void bfs(int start, int vertices) {
visited[start] = true;
printf("%d ", start);
enqueue(start);
while (front != -1) {
int v = dequeue();
for (int i = 0; i < vertices; i++) {
if (adjMatrix[v][i] == 1 && !visited[i]) {
visited[i] = true;
printf("%d ", i);
enqueue(i);
}
}
}
}
int main() {
int V = 5;
// ... 初始化图和添加边的代码 ...
printf("BFS遍历结果: ");
bfs(0, V);
return 0;
}
5. 图的应用
- 社交网络分析:使用图来表示人与人之间的关系,分析社交网络的结构。
- 路径规划:在地图应用中使用图来表示道路网络,计算最短路径。
- 网络拓扑:在计算机网络中使用图来表示网络结构,优化网络性能。
- 推荐系统:使用图来表示用户和商品之间的关系,进行个性化推荐。
- 生物信息学:用图来表示蛋白质之间的相互作用网络。
- 编译器优化:在编译器中使用图来表示程序的控制流,进行代码优化。
- 资源分配:在操作系统中使用图来表示资源依赖关系,避免死锁。
6. 高级图算法
- 最短路径算法:Dijkstra算法、Floyd-Warshall算法
- 最小生成树:Prim算法、Kruskal算法
- 强连通分量:Kosaraju算法、Tarjan算法
- 网络流:Ford-Fulkerson算法、Edmonds-Karp算法
- 图着色:回溯算法、贪心算法
7. 总结
图是一种强大而灵活的数据结构,能够表示各种复杂的关系和网络。掌握图的基本概念、表示方法和常用算法对于解决许多实际问题至关重要。随着大数据和人工智能的发展,图在数据分析、机器学习等领域的应用也越来越广泛。
深入理解图数据结构不仅能帮助我们更好地建模现实世界的问题,还能提高我们的算法设计和问题解决能力。在实际应用中,选择合适的图表示方法和算法,对于提高程序的效率和可扩展性都有重要影响。