图论是数据结构与算法中必须逾越的一座大山,适用范围非常广泛,当然也具有一定的难度,本人也是今年第一次学习图论的知识点,不能说掌握的很好,但我会尽可能的把我学会的知识点分享给大家,在写这篇博客之前我也查阅了很多大牛的图论分享,也算基本上涵盖了图论的重要知识点,如有错误还望各路大神斧正。另外以下程序都是由C语言编写的,海域。
首先我们简单认识一下图论,我举一个最简单的例子,就是我们常用的百度或者高德地图,我们在导航时,背后的本质就是图论的各种算法,例如是否有通路可以抵达,或者最短路径是哪条线路等,所以图论的应用是非常广泛的,包括全市公交线路,如何帮我我们快速找到最快抵达的路线,这都是图论的功劳。另外值得注意的是,在计算图论的复杂度的时候,我们不再用n进行计算了,而是要以图的顶点个数V和边的个数E进行计算。那么接下来我们就开始正式讲解。
图论主要有三种表现形式,分别是边的数组(Array of edges),临接矩阵(Adjacency matrix)和临接链表(Adjacency list)。其中临接矩阵是最好理解,也是大家一定要掌握的,而我个人比较喜欢临接链表,三种形式主要是数据储存的方式不同,但背后的理论是相似的,学会一个另外的都会变得很好理解。当然这三种我都会给大家一一介绍的,各位看官可以根据自己的需求选择相应的方法。
1. Array-of-edges
边的数组主要是以数组的形式去储存顶点和边的信息,具体说应该是二维数组。具体的程序细节都在注释里面,大家可以结合代码去理解。
typedef int Vertex;
typedef struct Edge* edge;
struct Edge {
Vertex v; // 每条边的起点编号
Vertex w; // 每条边的终点编号
};
typedef struct graph* Graph;
struct graph {
edge **edges; //指向结构体指针的指针
int numberOfV; //顶点的个数
int numberOfE; //边的个数
};
graph* newGraph(int numberOfV) { //创建一个新的图
graph* newGraph = malloc(sizeof (struct graph));
newGraph->edges = malloc(sizeof (edge*) * 100); //构建二维数组(这是二维数组的第一层,定义大小)
//初始化顶点和边的数量,边的数量要根据插入函数来决定
newGraph->numberOfE = 0;
newGraph->numberOfV = numberOfV;
return newGraph;
}
//添加一条边
void append_edge(graph *g, int v, int w){ // 输入每条边起点和终点的编号
edge *newEdge = malloc(sizeof (struct Edge));
newEdge->v = v;
newEdge->w = w;
g->edges[g->numberOfE] = newEdge; //由创建街道可知,Graph->edges是一个可以存储100个边的数组,这一步的意思是从第0个开始存储边,起点和终点都同时存入了
g->numberOfE++;
}
void showGraph (graph* g){
printf("number of vertex: %d\n", g->numberOfV);
printf("number of edges: %d\n", g->numberOfE);
for (int i = 0; i < g->numberOfE; i++) {
printf("edge[%d] v:%d - w:%d\n", i+1, g->edges[i]->v, g->edges[i]->w);
}
}
void freeGraph(graph* g){
for (int i = 0; i < g->numberOfE; i++)
free(g->edges[i]);
free(g->edges);
free(g);
}
int main(void){
graph *g = newGraph(6);
append_edge(g, 1, 2);
append_edge(g, 1, 3);
append_edge(g, 1, 4);
showGraph(g);
freeGraph(g);
}
2. Adjacency matrix
矩阵相信大家都不陌生了,矩阵可以非常简单的让我们画出一个图,具体操作其实很简单,就是两个点若没有边则填0,若有边则填1.本质上也是二维数组,但是边的数组是有边才输入顶点编号,而矩阵是所有顶点都要写出来,然后两两不断的做对比。
//插入/删除一条边的算法法度是O(1),直接找到改为1/0即可,查找是O(E)
//创建(初始化,存储)一个矩阵的算法法度是O(v^2),
typedef int Vertex;
typedef struct edge { //类似于一个房屋
Vertex v; // 每条边的起点编号
Vertex w; // 每条边的终点编号
}Edge;
typedef struct graph{ //类似于一个街道
int **matrix; //定义为二维数组
int number_of_v; //顶点的数量
int number_of_e; //边的数量
}Graph;
Graph *new_graph(int number_of_v){ //创建一个矩阵并将其初始化
Graph *new_graph = malloc(sizeof(struct graph));
new_graph->matrix = malloc(sizeof (int *) * number_of_v);
for (int i = 0; i < number_of_v; i++) { //二维数组的初始化和赋值必须用两个for循环
new_graph->matrix[i] = malloc(sizeof (int) * number_of_v); //这两个malloc就是建立矩阵的过程。00,01,02