图(邻接矩阵)
2016-02-13 10:57
381 查看
图有两种表示方法,邻接矩阵和邻接表,接下来我们讲解邻接矩阵和用c实现一个邻接矩阵.
我们先看一个图:
![](http://images2015.cnblogs.com/blog/719854/201602/719854-20160213083539605-1719832966.png)
我们想将这样一个图信息存储起来,我们有两个必须存储的数据,节点信息(a,b,c,d,e)和权值(3,5,4,1,6,7)和节点之间的关系.权值也就是路径.
邻接矩阵表示法,用两个数组表示,一个一维数组和一个二维数组.
一维数组存储节点信息,二维数组存储节点之间的关系.
将上图转换成一个邻接矩阵
![](http://images2015.cnblogs.com/blog/719854/201602/719854-20160213090603698-2113050073.png)
接下来我们看一看邻接矩阵是的结构体.
我们将node_type设为char类型,方便明了.但在输入字符后,我们通常回输出空格或者回车符,在向字符变量输入字符时,我们要将之前输入的空格或者回车符从缓冲区中清楚,这也就是程序中getchar()的用处.
我们先构造一个邻接矩阵
1)输入节点数和节点信息,构造上图的两个数组.(将二维数组初始化MAX)
2)输入某个节点的相邻节点和权值(通过下标找到在二维数组中相应的位置,将权值输入)
"// i 代表行数, j 是用来循环的, k 代表列数"下面的代码是将输入的边和权值的信息存储在二维数组中.
i 代表的是在一维数组中的节点的位置,也是二维数组中行.(例如:与a相邻的节点有两个,b和c.这时i=0,代表以为数组中第一个位置,j用来循环输入边和权值,因为我们不知道每个节点都有多少个相邻节点.当输入'#'号时结束输入.k是为了将输入的相邻节点与一维数组中的节点信息匹配,如果存在这个节点,这时将权值输入到二维数组中,而 i 是二维数组的行, k 是二维数组的列.然后我们将边数+1,因为a 的相邻节点是 b ,而 b 的相邻节点是 a, 这时我们对边增加了两次,所以在构造邻接矩阵最后将边数除以2.)
深度优先遍历
深度优先遍历是通过递归来实现,与二叉树的遍历有点像.
1)以某一节点开始,访问该节点
2)以该节点开始,重复1.(这里需要定义一个节点数大小的bool类型数组,来记录哪些节点访问过了,哪些节点没有访问过.)
3)当某个节点的邻接节点都访问过了,回退到上一个节点,访问上一个节点的其他相邻节点.
4)重复3.直至返回开始节点.
这是连通图的遍历方法,对于非连通图,我们只需循环调用递归,直至所有节点都访问过.
实现算法:
1)先创建一个visited数组,初始化为false.
2)调用遍历函数,实现递归.
3)当相邻节点为false时,以该节点进行递归.
4)否则返回上一节点
图例:
![](http://images2015.cnblogs.com/blog/719854/201602/719854-20160213104143339-365280294.png)
广度优先遍历
广度优先遍历通过队列实现.
1)从摸一节点开始,将该节点入队,找到该节点的所有相邻节点,将他们入队.
2)将该节点出队,再将队头节点的所有相邻节点入队.(这里也需要一个visited数组,已经入队过的节点不再入队.)
3)检查队列,对队头元素进行操作,出队.
如果队列有问题的同学,可以参考我之前写的队列,我把队列文件直接拷贝过来了.
源码
graph.c
我们先看一个图:
![](http://images2015.cnblogs.com/blog/719854/201602/719854-20160213083539605-1719832966.png)
我们想将这样一个图信息存储起来,我们有两个必须存储的数据,节点信息(a,b,c,d,e)和权值(3,5,4,1,6,7)和节点之间的关系.权值也就是路径.
邻接矩阵表示法,用两个数组表示,一个一维数组和一个二维数组.
一维数组存储节点信息,二维数组存储节点之间的关系.
将上图转换成一个邻接矩阵
![](http://images2015.cnblogs.com/blog/719854/201602/719854-20160213090603698-2113050073.png)
接下来我们看一看邻接矩阵是的结构体.
typedef struct matrix { node_type vertex[MAX_NUM];//节点信息 int arcs[MAX_NUM][MAX_NUM];//矩阵 int vertexs, brim;//节点数,边数 } Graph;
我们将node_type设为char类型,方便明了.但在输入字符后,我们通常回输出空格或者回车符,在向字符变量输入字符时,我们要将之前输入的空格或者回车符从缓冲区中清楚,这也就是程序中getchar()的用处.
我们先构造一个邻接矩阵
1)输入节点数和节点信息,构造上图的两个数组.(将二维数组初始化MAX)
2)输入某个节点的相邻节点和权值(通过下标找到在二维数组中相应的位置,将权值输入)
void g_create(Graph * graph) { int num; int i, j, k; char c; printf("输入节点个数:"); scanf("%d", &graph->vertexs); getchar();//接受回车键 printf("输入节点信息:"); for ( i = 0; i < graph->vertexs; i++ ) { scanf("%c", &graph->vertex[i]); getchar(); } for ( i = 0; i < graph->vertexs; i++ )//初始化矩阵 for ( j = 0; j < graph->vertexs; j++ ) graph->arcs[i][j] = MAX_VALUE; graph->brim = 0;//初始化边数 // i 代表行数, j 是用来循环的, k 代表列数 for ( i = 0; i < graph->vertexs; i++ ) { printf("输入与%c节点相邻的节点与权值,输入#号键结束\n", graph->vertex[i]); for ( j = 0; j < graph->vertexs; j++ ) { scanf("%c", &c); if ( c == '#' ) { getchar(); break; } scanf("%d", &num); for ( k = 0; k < graph->vertexs; k++ ) { if ( graph->vertex[k] != c ) continue; graph->arcs[i][k] = num; graph->brim++; } getchar(); } } graph->brim /= 2; }
"// i 代表行数, j 是用来循环的, k 代表列数"下面的代码是将输入的边和权值的信息存储在二维数组中.
i 代表的是在一维数组中的节点的位置,也是二维数组中行.(例如:与a相邻的节点有两个,b和c.这时i=0,代表以为数组中第一个位置,j用来循环输入边和权值,因为我们不知道每个节点都有多少个相邻节点.当输入'#'号时结束输入.k是为了将输入的相邻节点与一维数组中的节点信息匹配,如果存在这个节点,这时将权值输入到二维数组中,而 i 是二维数组的行, k 是二维数组的列.然后我们将边数+1,因为a 的相邻节点是 b ,而 b 的相邻节点是 a, 这时我们对边增加了两次,所以在构造邻接矩阵最后将边数除以2.)
深度优先遍历
深度优先遍历是通过递归来实现,与二叉树的遍历有点像.
1)以某一节点开始,访问该节点
2)以该节点开始,重复1.(这里需要定义一个节点数大小的bool类型数组,来记录哪些节点访问过了,哪些节点没有访问过.)
3)当某个节点的邻接节点都访问过了,回退到上一个节点,访问上一个节点的其他相邻节点.
4)重复3.直至返回开始节点.
这是连通图的遍历方法,对于非连通图,我们只需循环调用递归,直至所有节点都访问过.
实现算法:
1)先创建一个visited数组,初始化为false.
2)调用遍历函数,实现递归.
3)当相邻节点为false时,以该节点进行递归.
4)否则返回上一节点
//深度优先遍历 static void dfs_graph(Graph * graph, bool visited[], const int i); void g_depth_first_search(Graph * graph) { bool visited[graph->vertexs]; int i; for ( i = 0; i < graph->vertexs; i++ ) visited[i] = false; visited[0] = true; dfs_graph(graph, visited, 0); printf("\n"); } static void dfs_graph(Graph * graph, bool visited[], const int i) { int j; printf("%c\t", graph->vertex[i]); for ( j = 0; j < graph->vertexs; j++ )//依次检查矩阵 { if ( graph->arcs[i][j] != MAX_VALUE && !visited[j] )//i 代表矩阵的行, j 代表矩阵的列 { visited[j] = true; dfs_graph(graph, visited, j); } } }
图例:
![](http://images2015.cnblogs.com/blog/719854/201602/719854-20160213104143339-365280294.png)
广度优先遍历
广度优先遍历通过队列实现.
1)从摸一节点开始,将该节点入队,找到该节点的所有相邻节点,将他们入队.
2)将该节点出队,再将队头节点的所有相邻节点入队.(这里也需要一个visited数组,已经入队过的节点不再入队.)
3)检查队列,对队头元素进行操作,出队.
void g_breadth_first_search(Graph * graph) { Queue queue;//队列存储的是节点数组的下标(int) bool visited[graph->vertexs]; int i, pos; q_init(&queue); for ( i = 0; i < graph->vertexs; i++ ) visited[i] = false; visited[0] = true; q_push(&queue, 0); while ( !q_empty(&queue) ) { pos = q_front(&queue); printf("%c\t", graph->vertex[pos]); for ( i = 0; i < graph->vertexs; i++ )//把队头元素的邻接点入队 { if ( !visited[i] && graph->arcs[pos][i] != MAX_VALUE ) { visited[i] = true; q_push(&queue, i); } } q_pop(&queue); } printf("\n"); }
如果队列有问题的同学,可以参考我之前写的队列,我把队列文件直接拷贝过来了.
源码
graph.c
#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
#include <limits.h>
#include "aqueue.h"
#define MAX_VALUE INT_MAX
#define MAX_NUM 100
typedef char node_type;
typedef struct matrix { node_type vertex[MAX_NUM];//节点信息 int arcs[MAX_NUM][MAX_NUM];//矩阵 int vertexs, brim;//节点数,边数 } Graph;
void g_create(Graph * graph) { int num; int i, j, k; char c; printf("输入节点个数:"); scanf("%d", &graph->vertexs); getchar();//接受回车键 printf("输入节点信息:"); for ( i = 0; i < graph->vertexs; i++ ) { scanf("%c", &graph->vertex[i]); getchar(); } for ( i = 0; i < graph->vertexs; i++ )//初始化矩阵 for ( j = 0; j < graph->vertexs; j++ ) graph->arcs[i][j] = MAX_VALUE; graph->brim = 0;//初始化边数 // i 代表行数, j 是用来循环的, k 代表列数 for ( i = 0; i < graph->vertexs; i++ ) { printf("输入与%c节点相邻的节点与权值,输入#号键结束\n", graph->vertex[i]); for ( j = 0; j < graph->vertexs; j++ ) { scanf("%c", &c); if ( c == '#' ) { getchar(); break; } scanf("%d", &num); for ( k = 0; k < graph->vertexs; k++ ) { if ( graph->vertex[k] != c ) continue; graph->arcs[i][k] = num; graph->brim++; } getchar(); } } graph->brim /= 2; }
void g_printMatrix(Graph * graph)//打印矩阵状态
{
int i, j;
for ( i = 0; i < graph->vertexs; i++ )
{
for ( j = 0; j < graph->vertexs; j++ )
{
printf("%-10d ", graph->arcs[i][j]);
}
printf("\n");
}
}
//深度优先遍历
static void dfs_graph(Graph * graph, bool visited[], const int i);
void g_depth_first_search(Graph * graph)
{
bool visited[graph->vertexs];
int i;
for ( i = 0; i < graph->vertexs; i++ )
visited[i] = false;
visited[0] = true;
dfs_graph(graph, visited, 0);
printf("\n");
}
static void dfs_graph(Graph * graph, bool visited[], const int i)
{
int j;
printf("%c\t", graph->vertex[i]);
for ( j = 0; j < graph->vertexs; j++ )//依次检查矩阵
{
if ( graph->arcs[i][j] != MAX_VALUE && !visited[j] )//i 代表矩阵的行, j 代表矩阵的列
{
visited[j] = true;
dfs_graph(graph, visited, j);
}
}
}
//广度优先遍历
void g_breadth_first_search(Graph * graph)
{
Queue queue;//队列存储的是节点数组的下标(int)
bool visited[graph->vertexs];
int i, pos;
q_init(&queue);
for ( i = 0; i < graph->vertexs; i++ )
visited[i] = false;
visited[0] = true;
q_push(&queue, 0);
while ( !q_empty(&queue) )
{
pos = q_front(&queue);
printf("%c\t", graph->vertex[pos]);
for ( i = 0; i < graph->vertexs; i++ )//把队头元素的邻接点入队
{
if ( !visited[i] && graph->arcs[pos][i] != MAX_VALUE )
{
visited[i] = true;
q_push(&queue, i);
}
}
q_pop(&queue);
}
printf("\n");
}
int main(void)
{
Graph graph;
g_create(&graph);
g_printMatrix(&graph);
g_depth_first_search(&graph);
g_breadth_first_search(&graph);
return 0;
}
相关文章推荐
- pig: ERROR 1070
- 2776 寻找代表元(匈牙利算法)
- Android Fragment完全解析,关于碎片你所需知道的一切
- C#里面Attribute的使用方法
- dx11学习笔记-1.创建一个空窗口
- 《笨办法学Python》 第38课手记
- PHP操作Mysql疑问?
- Nmap
- Codeforces Round #342 (Div. 2) 625C K-special Tables(脑洞)
- 【翻译自mos文章】怎么startup/shutdown PDB?
- UITableView如何撤销移动操作
- UITableView如何撤销移动操作
- python socket 解码IP层
- UITableView如何撤销移动操作
- css命名规范
- uvaoj-10082:WERTYU
- 房间声学原理与Schroeder混响算法实现
- 亲戚
- pig实战演练:手机流量统计
- android布局 FrameLayout(帧布局)详解