图
图的概念
图:有多个前驱节点和多个后继节点
图(Graph*)是一种非线性数据结构,形式化描述为:其中,V={Vi | Vi ∈datatype, i=0,1,……,n-1} 是图中元素Vi(称为顶点Vertex )的集合,n=0时,V为空集,记为φ。
R是顶点之间的关系集,P(Vi,Vj)为顶点Vi与Vj之间是否存在路径的判定条件,即若Vi与Vj之间的路径存在,则关系< Vi,Vj >∈R。
无向图
设Vi、Vj为图中的两个顶点,若关系<Vi,Vj>无方向性,
即:
当<Vi,Vj>∈R时,必有<Vj,Vi>∈R,则称此时的图为无向图。
关系用(Vi,Vj)或(Vj,Vi)表示,
称为图中的一条边(Edge)(Vi,Vj)
有向图
1)有向图(Digraph)
设 Vi、Vj为图中的两个顶点,若关系**<** Vi,Vj**>**存在方向性,即:< Vi,Vj>≠<Vj,Vi >,则称相应的图为有向图。<Vi,Vj>∈*R,表示从顶点 Vi到Vj的一条弧(Arc):
< Vi,Vj > Vi为弧尾,Vj为弧头。
网
网(Network)若在图的关系<Vi,Vj>或(Vi,Vj)上附加一个值w
子图
设图G=(V,R)、G’=(V’,R’),若且,则称G’为G的子图。例5-1、例5-2中G1和G2的如下:
顶点的度
图的存储
由于图中顶点间的关系(弧或边)无规律,故对图的存储较之表和树要复杂些,需要根据图的具体应用来构造图的存储结构。常用的存储表示有“数组表示法”、“邻接表”、“十字链表”和“邻接多重表”。
图的数组表示法(邻接矩阵)
图的邻接表表示法
V0 -V2-V3-
V1 -V2-
V2 -V0-V1-V5-
V3 -V0-V4-V5-
V4 -V3-
V5 -V2-V3-
图的实例
图的创建
graph_t * GraphCreate(void)
{
graph_t *g;
//1.分配图的内存
if((g = (graph_t *)malloc(sizeof(*g)))==NULL){
printf("malloc memory graph error\n");
return NULL;
}
//2.对图中的数据赋值
for(int i=0;i<N;i++){
g->data[i] = i;
}
//3将顶点和顶点的关系初始化为0
for(int i=0;i<N;i++){
for(int j=0;j<N;j++){
g->relation[i][j] = 0;
}
}
return g;
}
输入顶点的关系
void GraphInputRelation(graph_t* g)
{
int i, j;
while (scanf("(%d,%d)", &i, &j) == 2) {
g->relation[i][j] = g->relation[j][i] = 1;
}
}
输出图
void GraphShow(graph_t* g)
{
printf(" ");
for (int i = 0; i < N; i++) {
printf("V%d ", g->data[i]);
}
putchar(10);
for (int i = 0; i < N; i++) {
printf("V%d ", g->data[i]);
for (int j = 0; j < N; j++) {
printf("%d ",g->relation[i][j]);
}
putchar(10);
}
}
图的整体代码
graph.h
#ifndef __GRAPH_H__
#define __GRAPH_H__
#include <stdio.h>
#include <stdlib.h>
#define N 6
#define datatype int
typedef struct{
datatype data[N];
int relation[N][N];
}graph_t;
graph_t * GraphCreate(void);
void GraphInputRelation(graph_t *g);
void GraphShow(graph_t *g);
#endif
graph.c
#include "graph.h"
graph_t* GraphCreate(void)
{
graph_t* g;
// 1.分配图的内存
if ((g = (graph_t*)malloc(sizeof(*g))) == NULL) {
printf("malloc memory graph error\n");
return NULL;
}
// 2.对图中的数据赋值
for (int i = 0; i < N; i++) {
g->data[i] = i;
}
// 3将顶点和顶点的关系初始化为0
for (int i = 0; i < N; i++) {
for (int j = 0; j < N; j++) {
g->relation[i][j] = 0;
}
}
return g;
}
void GraphInputRelation(graph_t* g)
{
int i, j;
while (scanf("(%d,%d)", &i, &j) == 2) {
g->relation[i][j] = g->relation[j][i] = 1;
}
}
void GraphShow(graph_t* g)
{
printf(" ");
for (int i = 0; i < N; i++) {
printf("V%d ", g->data[i]);
}
putchar(10);
for (int i = 0; i < N; i++) {
printf("V%d ", g->data[i]);
for (int j = 0; j < N; j++) {
printf("%d ",g->relation[i][j]);
}
putchar(10);
}
}
main.c
#include "graph.h"
int main(int argc,const char * argv[])
{
graph_t *g;
//1.创建图
g = GraphCreate();
if(g == NULL){
printf("graph create error\n");
return -1;
}
//2.输入关系
GraphInputRelation(g);
//3.输出图
GraphShow(g);
return 0;
}
执行的效果
图的搜索
深度优先
深度优先搜索(Depth First Search,简称DFS)
1.算法思路
类似树的先序遍历。设初始时,图中各顶点均未被访问,从图中某顶点(设为V0)出发,访问V0,然后搜索V0的一个邻接点Vi,若Vi未被访问,则访问之,再搜索Vi的一个邻接点(深度优先)……。若某顶点的邻接点全部访问完毕,则回溯(Backtracking)到它的上一顶点,然后再从此顶点又按深度优先的方法搜索下去,……,直到能访问的顶点都访问完毕为止。
eg:
深度优先的顺序:V0,V2,V1,V5,V3,V4
int dfs_r[N] = {0};
void GraphDFS(graph_t *g,int i)
{
//1.先将i节点的数据打印出来,并将vi节点标记为被访问过
printf("V%d\n",g->data[i]);
dfs_r[i] = 1;
//2.查找关系
for(int j=0;j<N;j++){
if(g->relation[i][j]==1 && dfs_r[j]==0){
GraphDFS(g,j);
}
}
}
深度优先搜索的顺序:A,B,D,C,E,F
int dfs_r[N] = {0};
void GraphDFS(graph_t *g,int i)
{
//1.先将i节点的数据打印出来,并将vi节点标记为被访问过
printf("%c\n",g->data[i]);
dfs_r[i] = 1;
//2.查找关系
for(int j=0;j<N;j++){
if(g->relation[i][j]==1 && dfs_r[j]==0){
GraphDFS(g,j);
}
}
}
广度优先
广度优先搜索(Breadth First Search),简称BFS。
1.算法思路
类似树的按层次遍历。初始时,图中各顶点均未被访问,从图中某顶点(设V0)出发,访问V0,并依次访问V0的各邻接点(广度优先)。然后,分别从这些被访问过的顶点出发,仍按照广度优先的策略搜索其它顶点,……,直到能访问的顶点都访问完毕为止。控制广度优先的正确搜索,要用到队列技术,即访问完一个顶点后,让该顶点的序号进队。然后取相应队头(出队),考察访问过的顶点的各邻接点,将未访问过的邻接点访问后再依次进队,……,直到队空为止。
eg:
广度优先:A,B,D,F,E,C
int bfs_r[N] = {0};
void GraphBFS(graph_t *g,int i)
{
linkqueue_t *q;
int pos;
//1.创建一个队列
q = LinkQueueCreate();
//2.让第0个节点入队
LinkQueueEnQueue(q,i);
bfs_r[i]=1;
//3.循环搜索,队列为空的时候
while(!LinkQueueIsEmpty(q)){
//4.出队
pos = LinkQueueDeQueue(q);
printf("%c\n",g->data[pos]);
//5.找关系
for(int j=0;j<N;j++){
if(g->relation[pos][j]==1 &&bfs_r[j]==0){
LinkQueueEnQueue(q,j);
bfs_r[j]=1;
}
}
}
}
图的整体的代码
graph.h
#ifndef __GRAPH_H__
#define __GRAPH_H__
#include <stdio.h>
#include <stdlib.h>
#include "linkqueue.h"
#define N 6
#define datatype int
typedef struct{
datatype data[N];
int relation[N][N];
}graph_t;
graph_t * GraphCreate(void);
void GraphInputRelation(graph_t *g);
void GraphShow(graph_t *g);
void GraphDFS(graph_t *g,int i);
void GraphBFS(graph_t *g,int i);
#endif
graph.c
#include "graph.h"
graph_t* GraphCreate(void)
{
graph_t* g;
// 1.分配图的内存
if ((g = (graph_t*)malloc(sizeof(*g))) == NULL) {
printf("malloc memory graph error\n");
return NULL;
}
// 2.对图中的数据赋值
for (int i = 0; i < N; i++) {
g->data[i] = 'A'+i;
}
// 3将顶点和顶点的关系初始化为0
for (int i = 0; i < N; i++) {
for (int j = 0; j < N; j++) {
g->relation[i][j] = 0;
}
}
return g;
}
void GraphInputRelation(graph_t* g)
{
int i, j;
while (scanf("(%d,%d)", &i, &j) == 2) {
g->relation[i][j] = g->relation[j][i] = 1;
}
}
void GraphShow(graph_t* g)
{
printf(" ");
for (int i = 0; i < N; i++) {
printf("%c ", g->data[i]);
}
putchar(10);
for (int i = 0; i < N; i++) {
printf("%c ", g->data[i]);
for (int j = 0; j < N; j++) {
printf("%d ",g->relation[i][j]);
}
putchar(10);
}
}
//通过dfs_r标记当前节点是否被访问过
//0没有被访问过,如果1表示被访问过
int dfs_r[N] = {0};
void GraphDFS(graph_t *g,int i)
{
//1.先将i节点的数据打印出来,并将vi节点标记为被访问过
printf("%c\n",g->data[i]);
dfs_r[i] = 1;
//2.查找关系
for(int j=0;j<N;j++){
if(g->relation[i][j]==1 && dfs_r[j]==0){
GraphDFS(g,j);
}
}
}
int bfs_r[N] = {0};
void GraphBFS(graph_t *g,int i)
{
linkqueue_t *q;
int pos;
//1.创建一个队列
q = LinkQueueCreate();
//2.让第0个节点入队
LinkQueueEnQueue(q,i);
bfs_r[i]=1;
//3.循环搜索,队列为空的时候
while(!LinkQueueIsEmpty(q)){
//4.出队
pos = LinkQueueDeQueue(q);
printf("%c\n",g->data[pos]);
//5.找关系
for(int j=0;j<N;j++){
if(g->relation[pos][j]==1 &&bfs_r[j]==0){
LinkQueueEnQueue(q,j);
bfs_r[j]=1;
}
}
}
}
main.c
#include "graph.h"
int main(int argc,const char * argv[])
{
graph_t *g;
//1.创建图
g = GraphCreate();
if(g == NULL){
printf("graph create error\n");
return -1;
}
//2.输入关系
GraphInputRelation(g);
//3.输出图
GraphShow(g);
//4.深度优先
GraphDFS(g,0);
puts("---------------------------");
//5.广度优先
GraphBFS(g,0);
return 0;
}