数据结构之图的原理及实现
一、C语言 数据结构之图 的源码实现及详解
以下是一个简单的C语言实现有向图的例子:
#include <stdio.h>
#include <stdlib.h>
#define MAX_VERTICES 100
typedef struct Vertex {
int data;
struct Vertex* next;
} Vertex;
typedef struct Graph {
Vertex* vertices[MAX_VERTICES];
int num_vertices;
} Graph;
void init_graph(Graph* graph) {
int i;
for (i = 0; i < MAX_VERTICES; i++) {
graph->vertices[i] = NULL;
}
graph->num_vertices = 0;
}
void add_vertex(Graph* graph, int data) {
Vertex* vertex = (Vertex*) malloc(sizeof(Vertex));
vertex->data = data;
vertex->next = NULL;
graph->vertices[graph->num_vertices++] = vertex;
}
void add_edge(Graph* graph, int from, int to) {
Vertex* vertex = graph->vertices[from];
while (vertex->next != NULL) {
vertex = vertex->next;
}
Vertex* edge = (Vertex*) malloc(sizeof(Vertex));
edge->data = to;
edge->next = NULL;
vertex->next = edge;
}
void print_graph(Graph* graph) {
int i;
for (i = 0; i < graph->num_vertices; i++) {
Vertex* vertex = graph->vertices[i];
printf("%d: ", vertex->data);
vertex = vertex->next;
while (vertex != NULL) {
printf("%d ", vertex->data);
vertex = vertex->next;
}
printf("\n");
}
}
int main() {
Graph graph;
init_graph(&graph);
add_vertex(&graph, 0);
add_vertex(&graph, 1);
add_vertex(&graph, 2);
add_edge(&graph, 0, 1);
add_edge(&graph, 1, 2);
add_edge(&graph, 2, 0);
print_graph(&graph);
return 0;
}
这个例子中,我们使用了两个结构体,Vertex和Graph。Vertex是图中的一个顶点,它包含一个数据域和一个指向下一个顶点的指针。Graph是整个图的结构,它包含了一个顶点指针数组和一个表示顶点数量的整数。
在init_graph函数中,我们初始化了顶点指针数组。在add_vertex函数中,我们为图添加了一个新的顶点。在add_edge函数中,我们为图添加了一条从一个顶点到另一个顶点的边。在print_graph函数中,我们遍历了整个顶点指针数组,并打印了每个顶点及其指向的其他顶点。
在main函数中,我们创建了一个Graph结构体,并添加了三个顶点和三条边。最后,我们调用print_graph函数打印整个图的结构。
二、C++ 语言 数据结构之图 的源码实现及详解
图的数据结构
邻接矩阵
邻接矩阵是图的一种表示方法,它利用一个矩阵来表示各个顶点之间的连接关系。设图G=(V, E)是一个有向或无向图,其中|V|=n,每个顶点都有一个编号 1~n,A是一个n*n的矩阵。对于无向图,如果顶点i和顶点j之间有边,那么A[i][j]=A[j][i]=1;否则A[i][j]=A[j][i]=0。对于有向图,如果顶点i到顶点j有一条边,那么A[i][j]=1;否则A[i][j]=0。
- 优点:可以快速判断任意两个顶点之间是否有边,适用于稠密图。
- 缺点:浪费内存空间,当图比较稀疏时存储效率较低。
邻接表
邻接表是图的另一种表示方法,它将每个顶点的所有邻接点存储在一个链表中。设图G=(V, E)是一个有向或无向图,其中|V|=n,每个顶点都有一个编号 1~n,用数组Edge表示邻接表。对于每个顶点i,Edge[i]存储该顶点的邻接点,每个邻接点用一个结构体表示,包括该邻接点的编号和指向下一个邻接点的指针(下图所示)。对于有向图,如果有一条从i到j的边,那么在Edge[i]这条链表中加入一个指向j的结点;对于无向图,需要在Edge[i]和Edge[j]两个链表中都加入相应的结点。
- 优点:节省存储空间,适用于稀疏图。
- 缺点:查询任意两个顶点之间是否有边的时间复杂度较高。
基本操作
创建图
const int MAXN = 1000;
int n, m; // 顶点数、边数
struct Edge {
int to, next; // to:边的终点,next:下一条边的编号
} edge[MAXN << 1];
int head[MAXN], cnt = 1;
void add_edge(int u, int v) { // 添加一条边
edge[++cnt].to = v;
edge[cnt].next = head[u];
head[u] = cnt;
}
void create_graph() { // 创建图
cin >> n >> m;
int u, v;
for (int i = 1; i <= m; i++) {
cin >> u >> v;
add_edge(u, v);
add_edge(v, u); // 无向图,加入反向边
}
}
遍历图
遍历图需要使用到图的一些基本算法,比如DFS(深度优先搜索)和BFS(广度优先搜索)。
DFS
深度优先搜索是一种搜索算法,它从图的某个顶点出发,尽可能深地探索该顶点邻接的所有顶点,直到不能继续探索为止,然后回溯到上一个顶点,继续探索其他顶点。DFS可以用递归或栈实现,下面给出用栈实现的代码:
bool vis[MAXN]; // 标记数组
void dfs(int u) {
vis[u] = true;
for (int i = head[u]; i; i = edge[i].next) {
int v = edge[i].to;
if (!vis[v]) dfs(v);
}
}
BFS
广度优先搜索是一种搜索算法,它从图的某个顶点出发,依次遍历该顶点的所有邻接点,然后遍历这些邻接点的所有未被访问过的邻接点,以此类推,直到所有顶点都被访问完为止。BFS通常用队列实现,下面给出用队列实现的代码:
bool vis[MAXN]; // 标记数组
void bfs(int s) {
queue<int> q;
vis[s] = true;
q.push(s); // 把起点加入队列
while (!q.empty()) { // 队列非空
int u = q.front();
q.pop();
for (int i = head[u]; i; i = edge[i].next) {
int v = edge[i].to;
if (!vis[v]) {
vis[v] = true;
q.push(v);
}
}
}
}
判断边与点的连通性
判断两个点是否联通,可以使用DFS或BFS遍历图,判断终点是否被访问过。判断一条边是否存在,可以遍历该边连接的两个点,看它们是否连通。
以下是判断两个点是否联通的代码:
bool vis[MAXN]; // 标记数组
bool check_connectivity(int u, int v) {
memset(vis, false, sizeof(vis));
dfs(u); // 从u出发遍历图
return vis[v]; // 判断v是否被访问过
}
以下是判断一条边是否存在的代码:
bool check_edge(int u, int v) {
for (int i = head[u]; i; i = edge[i].next) {
if (edge[i].to == v) return true;
}
return false;
}
三、java语言 数据结构之图 的源码实现及详解
Java语言中,图可以使用邻接表或邻接矩阵来实现。下面分别介绍这两种实现方式的源码实现及详解。
- 邻接表实现图
邻接表是一种图的表示方法,它是一个数组的链表,其中数组的每个元素表示图中的一个顶点,链表则存储相邻顶点的信息。邻接表实现图的优点是可以有效地存储稀疏图,即顶点数量远大于边的数量的图,缺点是查找相邻顶点的时间复杂度为O(n)。
下面是邻接表实现图的Java代码:
import java.util.ArrayList;
public class Graph {
private int vertices;
private ArrayList<ArrayList<Integer>> adjList;
public Graph(int vertices) {
this.vertices = vertices;
adjList = new ArrayList<>(vertices);
for (int i = 0; i < vertices; i++) {
adjList.add(new ArrayList<>());
}
}
public void addEdge(int u, int v) {
adjList.get(u).add(v);
adjList.get(v).add(u);
}
public void printGraph() {
for (int i = 0; i < vertices; i++) {
System.out.print(i + ": ");
for (int j = 0; j < adjList.get(i).size(); j++) {
System.out.print(adjList.get(i).get(j) + " ");
}
System.out.println();
}
}
}
在上面的代码中,Graph
类有两个成员变量:vertices
和adjList
。vertices
表示顶点的数量,adjList
是一个ArrayList
列表,列表中每个元素又是一个ArrayList
,用于存储与该顶点相邻的顶点。
addEdge
方法用于添加一条边,该方法将顶点u
和顶点v
加入它们各自的邻接表中。
printGraph
方法用于打印图的邻接表。
下面是使用邻接表实现图的代码示例:
public class Main {
public static void main(String[] args) {
Graph graph = new Graph(5);
graph.addEdge(0, 1);
graph.addEdge(0, 4);
graph.addEdge(1, 2);
graph.addEdge(1, 3);
graph.addEdge(1, 4);
graph.addEdge(2, 3);
graph.addEdge(3, 4);
graph.printGraph();
}
}
输出结果为:
0: 1 4
1: 0 2 3 4
2: 1 3
3: 1 2 4
4: 0 1 3
上面结果表示的图如下:
0-----1
/ \ / \
/ \ / \
4-----3-----2
- 邻接矩阵实现图
邻接矩阵是一种二维数组,其中数组的每个元素表示一条边,每个元素的值表示该边的权重或存在性。邻接矩阵实现图的优点是可以快速查找相邻顶点,时间复杂度为O(1),但是相对于邻接表,邻接矩阵存储稀疏图时比较浪费空间。
下面是邻接矩阵实现图的Java代码:
public class Graph {
private int vertices;
private int[][] adjMatrix;
public Graph(int vertices) {
this.vertices = vertices;
adjMatrix = new int[vertices][vertices];
}
public void addEdge(int u, int v) {
adjMatrix[u][v] = 1;
adjMatrix[v][u] = 1;
}
public void printGraph() {
for (int i = 0; i < vertices; i++) {
for (int j = 0; j < vertices; j++) {
System.out.print(adjMatrix[i][j] + " ");
}
System.out.println();
}
}
}
在上面的代码中,Graph
类有两个成员变量:vertices
和adjMatrix
。vertices
表示顶点的数量,adjMatrix
是一个int
类型的二维数组,用于存储图的邻接矩阵。
addEdge
方法用于添加一条边,该方法将顶点u
和顶点v
在邻接矩阵中的对应位置设为1。
printGraph
方法用于打印图的邻接矩阵。
下面是使用邻接矩阵实现图的代码示例:
public class Main {
public static void main(String[] args) {
Graph graph = new Graph(5);
graph.addEdge(0, 1);
graph.addEdge(0, 4);
graph.addEdge(1, 2);
graph.addEdge(1, 3);
graph.addEdge(1, 4);
graph.addEdge(2, 3);
graph.addEdge(3, 4);
graph.printGraph();
}
}
输出结果为:
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
上面结果表示的图如下:
0-----1
/ \ / \
/ \ / \
4-----3-----2
以上就是Java语言中实现图的邻接表和邻接矩阵两种方式的源码实现及详解。