数据结构㉓图的邻接矩阵结构及遍历

 

图的邻接矩阵结构及遍历...



定义

图的邻接矩阵存储方式是用两个数组来表示图。一个一维数组存储图中顶点信息,一个二维数组(称为邻接矩阵)存储图中的边。

设图A=(V,E)是一个有n个顶点的图,图的邻接矩阵为Egde[n][n],则



注:W为权值,当不需要权值时,取w为1表示结点间的连接。



  • 无向图的邻接矩阵是对称的
  • 有向图的邻接矩阵是不对称的


邻接矩阵的结构

头结点

typedef struct _tag_MGraph {

int count;

//存储图的结点

MVertex** v;

//邻接矩阵存储边

int** matrix;
} TMGraph;
  • count记录顶点个数
  • v记录顶点信息
  • matrix邻接矩阵存储图中顶点间的边


动态申请二位数组

  • 通过二级指针动态申请一维指针数组
  • 通过一级指针申请数据空间
  • 将一维指针数组中的指针连接到数据空间


int** malloc2d(int row, int col) {

int** ret = (int**)malloc(sizeof(int*) * row);

int* p = (int*)malloc(sizeof(int) * row * col);

int i = 0;

if (p && ret) {

for(i = 0; i < row; i++) {

ret = p + i * col;

}

} else {

free(ret);

free(p);

ret = NULL;

}

return ret;
}



图的遍历

从图中的某一顶点出发访遍图中的所有顶点,使得每个顶点仅被访问一次



深度优先遍历(DFS)

也称深度优先搜索,Depth First Search





整个过程需要一个标记顶点是否被访问过的数组visited[]



算法描述

  1. 访问起始顶点v
  2. 当v还有邻接顶点未访问时,深度遍历未访问过的邻接顶点w
  3. 当v的所有邻接顶点都被访问时,若图中所有顶点都被访问则算法结束;若图中还有未被访问的结点,则递归进行1,2步骤。


广度优先遍历(BFS)

也称广度优先搜索,Breadth First Search





整个过程需要一个标记顶点是否被访问过的数组visited[]



算法描述

  1. 访问起始顶点V0
  2. 依次访问V0的各个邻接顶点V01,V02...
  3. 依次访问V01,V02...的各个邻接顶点
  4. 若图中的所有结点均已访问则算法结束,否则访问下一顶点V2,重复上面的步骤。


代码实现

代码中使用的队列请参考数据结构⑬链式队列的优化中的队列



头文件

//
// Created by 刘涤生 on 2017/2/28 21:00.
//
#ifndef CPLUSPLUS_MGraph_H
#define CPLUSPLUS_MGraph_H
typedef void MGraph;
typedef void MVertex;
typedef void (MGraph_Printf)(MVertex*);
/**

* 创建并返回有n个顶点的图

* @param n

* @return

*/
MGraph* MGraph_create(MVertex** v,int n);
/**

* 销毁图

* @param MGraph

*/
void MGraph_destroy(MGraph* MGraph);
/**

* 清空图

* @param MGraph

*/
void MGraph_clear(MGraph* MGraph);
/**

* 在顶点v1和v2之间加上边

* @param MGraph

* @param v1

* @param v2

* @param w 边的权值

* @return

*/
int MGraph_add_edge(MGraph* MGraph,int v1,int v2,int w);
/**

* 将顶点v1和v2之间的边删除

* @param MGraph

* @param v1

* @param v2

* @return 权值

*/
int MGraph_remove_edge(MGraph* MGraph,int v1,int v2);
/**

* 获取顶点v1和v2之间的边的权值

* @param MGraph

* @param v1

* @param v2

* @return

*/
int MGraph_get_edge(MGraph* MGraph,int v1,int v2);
/**

* 获取图中V顶点的度

* @param MGraph

* @param v

* @return

*/
int MGraph_degree(MGraph* MGraph,int v);
/**

* 获取图中的结点数

* @param MGraph

* @return

*/
int MGraph_vertex_count(MGraph* MGraph);
/**

* 获取图中的边数

* @param MGraph

* @return

*/
int MGraph_edge_count(MGraph* MGraph);
/**

* 深度优先遍历

* @param graph

* @param v 顶点的位置

* @param pFunc

*/
void MGraph_DFS(MGraph* graph,int v,MGraph_Printf* pFunc);
/**

* 广度优先遍历

* @param graph

* @param v

* @param pFunc

*/
void MGraph_BFS(MGraph* graph,int v,MGraph_Printf* pFunc);
/**

* 打印图

* @param graph

* @param pFunc 指针打印函数

* @return

*/
void MGraph_Display(MGraph* graph,MGraph_Printf* pFunc);
#endif
实现文件

//
// 邻接矩阵法实现图
// Created by 刘涤生 on 2017/2/28 21:00.
//
#include "MGraph.h"
#include "LinkQueue.h"
#include

#include

typedef struct _tag_MGraph {

int count;

//存储图的结点

MVertex** v;

//邻接矩阵存储边

int** matrix;
} TMGraph;
static void recursive_dfs(TMGraph* graph,int v,int visited[],MGraph_Printf* pFunc) {

//访问顶点

int i = 0;

pFunc(graph->v[v]);

//遍历后置为1

visited[v] = 1;

printf(", ");

//遍历顶点v相关联的顶点

for(i = 0; i < graph->count; i++) {

//顶点间需要有边

if ((graph->matrix[v] != 0) && !visited) {

recursive_dfs(graph,i,visited,pFunc);

}

}

}

static void bfs(TMGraph* graph,int v,int visited[],MGraph_Printf* pFunc) {

LinkQueue* queue = LinkQueue_Create();

if (queue != NULL) {

//将该顶点及其相关的顶点依次存入队列中

LinkQueue_Offer(queue,graph->v + v);

visited[v] = 1;

while (LinkQueue_Size(queue) > 0) {

int i = 0;

//取出序号

v = (MVertex**)LinkQueue_Poll(queue) - graph->v;

pFunc(graph->v[v]);

printf(", ");

//遍历该顶点中邻接链表中的结点

for(i = 0; i < graph->count; i++) {

if ((graph->matrix[v]) && !visited) {

LinkQueue_Offer(queue,graph->v + i);

visited = 1;

}

}

}

}

LinkQueue_Destroy(queue);
}

//o(n)
MGraph* MGraph_create(MVertex** v,int n) {

TMGraph* ret = NULL;

if ((v != NULL) && (n > 0)) {

ret = (TMGraph*)malloc(sizeof(TMGraph));

if (ret != NULL) {

int * p = NULL;

ret->count = n;

ret->v = (MVertex**)malloc(sizeof(MVertex*) * n);

ret->matrix = (int**)malloc(sizeof(int*) * n);

//动态申请数据空间

p = (int*)calloc(n * n,sizeof(int));

if ((ret->v != NULL) && (ret->matrix != NULL) && (p != NULL)) {

int i = 0;

for (i = 0; i < n; i++) {

//连接到数据空间

ret->v = v;

ret->matrix = p + i * n;

}

} else {

free(p);

free(ret->matrix);

free(ret->v);

free(ret);

}

}

}

return ret;
}

//o(n)
void MGraph_destroy(MGraph* graph) {

TMGraph* tGraph = (TMGraph*)graph;

if (tGraph != NULL) {

//顺序不能乱

free(tGraph->v);

//释放数据空间

free(tGraph->matrix[0]);

free(tGraph->matrix);

free(tGraph);

}
}

//o(n^2)
void MGraph_clear(MGraph* graph) {

TMGraph* tGraph = (TMGraph*)graph;

if (tGraph != NULL) {

int i = 0;

int j = 0;

for (i = 0; i < tGraph->count; i++) {

for (j = 0; j < tGraph->count; j++) {

tGraph->matrix[j] = 0;

}

}

}
}

//O(1)
int MGraph_add_edge(MGraph* graph,int v1,int v2,int w) {

TMGraph* tGraph = (TMGraph*)graph;

int ret = (tGraph != NULL);

ret = ret && (0 count);

ret = ret && (0 count);

ret = ret && (0 matrix[v1][v2] = w;

}

return ret;
}

//O(1)
int MGraph_remove_edge(MGraph* graph, int v1, int v2) {

int ret = MGraph_get_edge(graph,v1,v2);

if (ret != 0) {

((TMGraph*)graph)->matrix[v1][v2] = 0;

}

return ret;
}

//O(1)
int MGraph_get_edge(MGraph* graph,int v1,int v2) {

TMGraph* tGraph = (TMGraph*)graph;

int condition = (tGraph != NULL);

condition = condition && (0 count);

condition = condition && (0 count);

int ret = 0;

if (condition) {

ret = tGraph->matrix[v1][v2];

}

return ret;
}

//O(n)
int MGraph_degree(MGraph* graph,int v) {

TMGraph* tGraph = (TMGraph*)graph;

int condition = (tGraph != NULL);

condition = condition && (0 count);

int ret = 0;

if (condition) {

int i = 0;

for (i = 0; i < tGraph->count; i++) {

if (tGraph->matrix[v] != 0) {

ret++;

}

if (tGraph->matrix[v] != 0) {

ret++;

}

}

}

return ret;
}

//O(1)
int MGraph_vertex_count(MGraph* graph) {

TMGraph* tGraph = (TMGraph*)graph;

int ret = 0;

if (tGraph != NULL) {

ret = tGraph->count;

}

return ret;
}

//O(n^2)
int MGraph_edge_count(MGraph* graph) {

TMGraph* tGraph = (TMGraph*)graph;

int ret = 0;

if (tGraph != NULL) {

int i = 0;

int j = 0;

for (i = 0; i < tGraph->count; i++) {

for (j = 0; j < tGraph->count; j++) {

if(tGraph->matrix[j] != 0) {

//如果是无向图 ret除以2

ret++;

}

}

}

}

return ret;
}

void MGraph_DFS(MGraph* graph,int v,MGraph_Printf* pFunc) {

TMGraph *tGraph = (TMGraph *) graph;

int* visited = (int*)calloc(tGraph->count,sizeof(int));

int condition = (tGraph != NULL);

condition = condition && (0 count);

condition = condition && (pFunc != NULL);

condition = condition && (visited != NULL);

if (condition) {

recursive_dfs(tGraph,v,visited,pFunc);

//遍历其他顶点

int i = 0;

for (i = 0; i < tGraph->count; i++) {

if (!visited) {

recursive_dfs(graph,i,visited,pFunc);

}

}

}

free(visited);
}

void MGraph_BFS(MGraph* graph,int v,MGraph_Printf* pFunc) {

TMGraph *tGraph = (TMGraph *) graph;

int* visited = (int*)calloc(tGraph->count,sizeof(int));

int condition = (tGraph != NULL);

condition = condition && (0 count);

condition = condition && (pFunc != NULL);

condition = condition && (visited != NULL);

if (condition) {

bfs(graph,v,visited,pFunc);

//遍历其他顶点

int i = 0;

for (i = 0; i < tGraph->count; i++) {

if (!visited) {

bfs(graph,i,visited,pFunc);

}

}

}

free(visited);
}

//O(n^2)
void MGraph_Display(MGraph* graph,MGraph_Printf* pFunc) {

TMGraph* tGraph = (TMGraph*)graph;

if ((tGraph != NULL) && ( pFunc != NULL)) {

int i = 0;

int j = 0;

for (int i = 0; i < tGraph->count; i++) {

printf("%d:",i);

pFunc(tGraph->v);

printf(" ");

}

printf("");

printf("边及权值");

for (int i = 0; i < tGraph->count; i++) {

for (int j = 0; j < tGraph->count; j++) {

if (tGraph ->matrix[j] != 0) {

printf("");

printf(" ");

}

}

}

printf("");

}
}测试文件

//
// Created by 刘涤生 on 2017/1/11 21:47.
//
#include

#include "graph/MGraph.h"
void print_data(MVertex* mVertex) {

printf("%s",(char*)mVertex);
}

int main() {

MVertex* v[] = {"A","B","C","D","E"};

MGraph* graph = MGraph_create(v,5);

MGraph_add_edge(graph,0,1,1);

MGraph_add_edge(graph,1,2,1);

MGraph_add_edge(graph,2,3,1);

MGraph_add_edge(graph,2,4,1);

MGraph_add_edge(graph,3,4,9);

printf("顶点间的权值 = %d
",MGraph_get_edge(graph,2,3));

printf("图中边的数量 = %d
",MGraph_edge_count(graph));

printf("结点数 = %d
",MGraph_vertex_count(graph));

printf("顶点的度 = %d
",MGraph_degree(graph,1));

printf("打印图");

MGraph_Display(graph,print_data);

printf("从C开始图的深度优先遍历");

MGraph_DFS(graph,2,print_data);

printf("");

printf("从B开始图的广度优先遍历");

MGraph_BFS(graph,1,print_data);

printf("");

printf("删除边");

MGraph_remove_edge(graph,2,3);

MGraph_Display(graph,print_data);

MGraph_clear(graph);

printf("结点数 = %d
",MGraph_vertex_count(graph));

printf("图中边的数量 = %d
",MGraph_edge_count(graph));

printf("顶点的度 = %d
",MGraph_degree(graph,1));

MGraph_destroy(graph);
}

顶点间的权值 = 1
图中边的数量 = 5
结点数 = 5
顶点的度 = 2
打印图
0:A 1:B 2:C 3:D 4:E
边及权值

从C开始图的深度优先遍历
C, D, E, A, B,
从B开始图的广度优先遍历
B, C, D, E, A,
删除边
0:A 1:B 2:C 3:D 4:E
边及权值

结点数 = 5
图中边的数量 = 0
顶点的度 = 0小结

  • 邻接矩阵法优点:直观容易实现
  • 缺点:当顶点较多而边数较少时浪费时间和空间


    关注 刘涤生


微信扫一扫关注公众号

0 个评论

要回复文章请先登录注册