1. 图的表示
图有两种标准的表示方法:邻接矩阵表示、邻接表表示。
下面有两个例子:
无向图用两种方式表示:
有向图用两种方式表示:
注意
无向图的邻接矩阵是对称矩阵,存储时可以只存上三角或下三角。
带权图的表示
对于邻接表
- 链表结构节点增加一个域,表示权重
对于邻接矩阵
- 矩阵中元素不再只是用
0,1
表示,往往用∞
表示不连通,其它值表示连接权值。
结构选择
邻接表
- 在表示稀疏图时非常紧凑,而成为经常地选择;
邻接矩阵
- 邻接矩阵在表示稠密图时是常见的选择;如果需要快速判断任意两点之间是否有边相连,也可选择邻接矩阵。
2. 图的遍历
我们将对下面这个图进行遍历:
(1)邻接表结构的遍历
上图的邻接表存储结构:
根据这张邻接表,我们可以知道,它的:
- BFS结果:1,2,5,3,4
- DFS结果:1,2,5,4,3
下面是BFS,DFS代码示例,为了让你更关注搜索过程,下面没有列出了两个过程:
- 队列相关操作代码
- 创建图的过程代码
Code Example:
#include "stdio.h"
#include "stdlib.h"
#include "queue.h"
/**
* 图的邻接表存储结构
* 这里区分两个概念:顶点、边节点。
* 在邻接表的存储方式中:顶点以数组方式存储,边节点以链表方式存储。
*/
#define MAX_VERTEX_NUM 20 // 最大顶点数
#define True 1
#define False 0
typedef int bool;
bool visited[MAX_VERTEX_NUM]; // 记录每个节点是否被访问过
// 定义边节点
typedef struct EdgeNode {
int adj_vertex; // 该边指向的顶点位置
int weight; // 该边上的权值
struct EdgeNode *next; // 指向下一条边的指针
} EdgeNode;
// 定义顶点
typedef struct {
char data; // 顶点数据
EdgeNode *first_edge; // 顶点指向的第一个边节点
} VertexNode;
// 定义整个图
typedef struct {
VertexNode *vertex_nodes[MAX_VERTEX_NUM]; // 顶点数组
int edge_num, vertex_num; // 边数量,顶点数量
} AdjListGraph;
/**
* (邻接表)广度优先遍历图
* (BFS需要设置一个队列,队列存放访问过的顶点位置)
*/
void BFSTraverse(AdjListGraph *graph) {
/**
* 对整个图进行BFS遍历
* (整个图可能是非连通的,即包含多个连通子图)
*/
// 初始化标志数组
for (int i = 0; i <= graph->vertex_num; ++i) {
visited[i] = False;
}
// 定义队列,并初始化
Queue *queue = malloc(sizeof(Queue));
init_queue(queue);
// 以每个顶点为起点,遍历图
for (int j = 1; j <= graph->vertex_num; ++j) {
if (!visited[j]) {
VertexNode *vertex = graph->vertex_nodes[j]; // 获得当前节点
visited[j] = True; // 设置标志
printf("%c ", vertex->data); // 打印顶点
enqueue(queue, j); // 入队
// 寻找未访问过的节点
while (!queue_empty(queue)) {
int k = dequeue(queue); // 出队,k表示当前顶点位置
EdgeNode *head = graph->vertex_nodes[k]->first_edge; // 获得当前顶点的边节点链表头指针
EdgeNode *cur_edge = head; // 当前边节点
while (cur_edge) {
if (!visited[cur_edge->adj_vertex]) {
printf("%c ", graph->vertex_nodes[cur_edge->adj_vertex]->data); // 访问
visited[cur_edge->adj_vertex] = True;
enqueue(queue, cur_edge->adj_vertex);
}
cur_edge = cur_edge->next;
}
}
}
}
}
/**
* (邻接表)深度优先遍历图
*/
void DFS(AdjListGraph *graph, int i) {
/**
* 从邻接表表示的图中第i个节点开始,深度优先遍历图
* (这个函数只能完全遍历一个连通图)
*/
if (graph != NULL) {
// 标志位置1,表示访问过
visited[i] = True;
// 取出当前顶点
VertexNode *cur_vertex = graph->vertex_nodes[i];
printf("%c ", cur_vertex->data); // 输出当前节点数值
EdgeNode *head = cur_vertex->first_edge; // 获得当前顶点的边节点链表头指针
// 寻找未访问过的节点,如果找到,递归下去
EdgeNode *cur_edge = head;
while (cur_edge) {
if (!visited[cur_edge->adj_vertex]) { // 如果没有被访问过
DFS(graph, cur_edge->adj_vertex);
}
cur_edge = cur_edge->next;
}
}
}
void DFSTraverse(AdjListGraph *graph) {
/**
* 对整个图进行遍历
* (整个图可能是非连通的,即包含多个连通子图)
*/
// 初始化标志数组
for (int i = 1; i <= graph->vertex_num; ++i) {
visited[i] = False;
}
// 以每个顶点为起点,遍历图
for (int j = 1; j <= graph->vertex_num; ++j) {
if (!visited[j]) {
DFS(graph, j);
}
}
}
/**
* 主函数测试
*/
int main() {
AdjListGraph *graph = malloc(sizeof(AdjListGraph));
// 创建图
graph = create_graph(graph); // 创建图的代码后面给出
// BFS
printf("广度优先遍历结果:");
BFSTraverse(graph);
// DFS
printf("\n深度优先遍历结果:");
DFSTraverse(graph);
}
Output:
广度优先遍历结果:1 2 5 3 4
深度优先遍历结果:1 2 5 4 3
(2)邻接矩阵结构的遍历
Ref
http://blog.csdn.net/jnu_simba/article/details/8868235
http://blog.csdn.net/jnu_simba/article/details/8867687