1.定义:图是一种非线性数据结构,形式化描述为
Graph = {V,R};
其中,V={Vi|Vi属于datatype,i=0,1,2,……n-1}是图中元素Vi(称为顶点vertex)的集合,当n=0时,V为空集。
R = {<Vi,Vj> | Vi,Vj,属于V,且Vi,Vj之间存在路径,0<=i,j<=n-1}是顶点之间的关系集,<Vi,Vj>为顶点Vi,
Vj之间是否存在路径的判定条件,即若Vi,Vj之间的路径存在,则关系<Vi,Vj>属于R。
有向图:弧 无向图:边
网:若在图的关系<Vi,Vj>或者是<Vj,Vi>上面附加一个值W,那么w就为弧或边的权。带权的图就称为网{权w的具体含义视图在不同领域
的应用而定。如顶点表示城市,权表示两个城市之间的距离}
顶点的度:顶点的边或者是弧的条数
图的表示方式
数组表示法(邻接矩阵),邻接表,十字链表 邻接多重表
1.数组表示法
G=(V,R)。
可以用两个数组来存储图G,一个一维数组存储顶点集V,一个二维数组存储G中顶点间的关系R,该二维数组就是所谓的邻接矩阵
typedef char Vtype;//顶点的数据类型
typedef int Adjtype;//邻接矩阵的类型,其值就是边的权值的类型
typedef struct
{
Vtype V[MAX];//顶点的集合
Adjtype A[MAX][MAX];//边的集合,二维数组表示
int Vexnum;//顶点的个数
int Adjnum;//边的个数
}mgraph;
2.邻接表
将图中的每一个顶点V和由V出发的弧或边构成一个单链表,节点中保留他所指向的节点的下标,邻接表是图的一种链式存储结构
struct adj
{
int terminder;//该弧所指向的顶点的位置
char name;//指向的顶点的名字
struct adj *next;//指向下一条依附该顶点的弧的指针
}
struct Vertex
{
Vtype data;顶点信息
struct adj *first;指向第一条依附该顶点的弧的指针。
}
struct Vertex graph[MAX];
图的遍历
图的遍历是树的遍历的推广,是按照某种规则或者是次序访问图中各顶点一次且仅一次的操作,亦是将网状结构按照某种规则线性化的过程。对图的遍历常用的有“深度优先搜索”和“广度优先搜索”,二者是人工智能(AI)的基础.
1.深度优先搜索(DFS:Depth First Search)
若初始化时,图中的各顶点均未被访问,从图中某个节点出发,然后又按照深度优先搜索的方式搜索下去,直到能够访问的顶点
都能够访问完毕为止。
2.广度优先搜索(BFS:BreadthFirst Search)
类似于树的按层次遍历,初始时,图中各顶点均未被访问,从图中某顶点(v0)出发,访问之,并以此访问其余的各邻接点。然后,
分别从这些被访问的顶点出发,仍按照广度优先搜索的顺序访问,直到被访问的点都访问完毕为止。
下面的矩形图为邻接矩阵:
深度优先搜索:
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#define MAX 100
#define VERYBIG 65535
typedef char Vtype;//顶点的数据类型
typedef int Adjtype;//邻接矩阵的类型,其值就是边的权值的类型
typedef struct
{
Vtype V[MAX];//顶点的集合
Adjtype A[MAX][MAX];//边的集合,二维数组表示
int Vexnum;//顶点的个数
int Adjnum;//边的个数
}Graph;
/*
从g的顶点集合中寻找ch,如果找到了返回ch的下标,没有找到返回-1;
*/
int my_index(Graph *g,char ch)
{
int i;
for(i = 0;i < g->Vexnum;i++)
{
if(g->V[i] == ch)
return i;
}
return -1;
}
Graph *CreateGraph()
{
int i,j;
//先新建一张空图
Graph *g = (Graph *)malloc(sizeof(*g));
g->Vexnum = g->Adjnum = 0;
//确定顶点个数
scanf("%d",&g->Vexnum);
getchar(); //接收回车符
//输入每一个顶点的名称
for(i = 0;i < g->Vexnum;i++)
scanf("%c",&g->V[i]);
//把邻接矩阵全部置为无穷大
for(i = 0;i < g->Vexnum;i++)
{
for(j = 0;j < g->Vexnum;j++)
{
g->A[i][j] = VERYBIG;
}
}
//循环从键盘输入起点,终点,权值
Vtype from,to;
Adjtype weight;
while(1)
{
getchar();
scanf("%c%c%d",&from,&to,&weight);
if(from == '#')
break;
i = my_index(g,from);
j = my_index(g,to);
if(i != -1 && j != -1)
{
g->A[i][j] = weight;
g->Adjnum++;
}
}
return g;
}
//1.深度优先搜索(DFS:Depth First Search)
//找第row行的第一个邻接点
int First(Graph *g, int row)
{
int i;
for(i = 0;i < g->Vexnum;i++)
{
if(g->A[row][i] != VERYBIG)
{
return i;
}
}
return -1;
}
//找第row行的column+1列开始的下一个邻接点
int Next(Graph *g,int row,int column)
{
int i;
for(i = column+1;i < g->Vexnum;i++)
{
if(g->A[row][i] != VERYBIG)
{
return i;
}
}
return -1;
}
int visited[MAX];//0表示未被访问过,1表示已经被访问过
void DFS(Graph *g,int n)
{
visited[n] = 1;
printf("%c",g->V[n]);
int i;
for(i = First(g,n); i != -1;i = Next(g,n,i))
{
if(visited[i] == 0)
{
DFS(g,i);
}
}
}
void DFSVisit(Graph *g)
{
int i;
//将所有顶点都置为未被访问过
for(i = 0;i < g->Vexnum;i++)
{
visited[i] = 0;
}
//循环是为了防止孤立顶点未被访问
for(i = 0;i < g->Vexnum;i++)
{
if(visited[i] == 0)
{
DFS(g,i);//访问i节点
}
}
printf("\n");
}
//注意遍历部分打印出来的是abecfdg,打印部分打印出来的是图的那张表,是分开的两步;
void PrintGraph(Graph *g)
{
int i,j;
for(i = 0;i <g->Vexnum;i++)
printf("%c\t",g->V[i]);
printf("\n");
for(i = 0;i < g->Vexnum;i++)
{
for(j = 0;j < g->Vexnum;j++)
{
if(g->A[i][j] == VERYBIG)
{
printf("%c\t",42); //打印无穷大,用*代替
}
else
{
printf("%d\t",g->A[i][j]);
}
}
printf("\n");
}
}
最短路径问题
解决带权有向图中两个顶点间最短路径问题。
有两个经典算法;
Dijkstra(迪杰斯特拉)算法,Floyd(弗洛依德)算法
Dijkstra(迪杰斯特拉)算法;是解决从网络中任意顶点(源点)出发,求它到其他顶点(终点)的最短路径
Dijkstra(迪杰斯特拉)算法需要用到三个辅助变量:
1)向量s[n],
s[i] = 1 表示从源点V到该点Vi的最短路径已经求出来了,
s[0] = 0 表示从源点V到该点Vi的最短路径没有求出来,
初始时,s[v] = 1,其他s[i] = 0;
2)向量dist[n]
dist[i]存放从源点v到Vi这个点(当前的)最短路径的长度
初始化时,
dist[i] = <V,Vi>的权w,当v可以到达vi时;
dist[i] = 无穷大,当V不可以直接到Vi;
3)向量path[n]
path[i]存放从源点v到v[i]的(当前)最短路径
算法思路:
1)显然,从源点V到其他各顶点的第一条最短路径长度dist[u];
dist[u] = min{dist[w]|w =0,1,2,3……且S[w] = 0};
表示在所有未求出的当前路径中找出一条最短的,其长度作为当前求出的最短路径。
2)对s[w] = 0 的w,如果dist[u] + <u,w> < dist[w];
那么dist[w]被更新为 dist[w] = dist[u] + <u,w>;
然后重复步骤1,2,直到所有的s[n]都等于1为止
//求最短路径部分;
int s[MAX];
int s[MAX];
int dist[MAX];
char path[MAX][MAX];
void Dijkstra(Graph *g, int v)
{
int i,j;
for(i = 0;i < g->Vexnum;i++)
{
s[i] = 0;
dist[i] = g->A[v][i];
path[i][0] = g->V[v];
}
s[v] = 1;
int min;
int index;
for(i = 0;i < g->Vexnum-1;i++)
{
min = VERYBIG + 1;
index = -1;
for(j = 0;j < g->Vexnum;j++)
{
if(s[j] == 0 && dist[j] < min)
{
min = dist[j];
index = j;
}
}
//printf("%d:%d\n",i,min);
s[index] = 1;
path[index][strlen(path[index])] = g->V[index];
for(j = 0;j < g->Vexnum;j++)
{
if(dist[index] + g->A[index][j] < dist[j])
{
if(s[j] == 0)
{
dist[j] = min + g->A[index][j];
strcpy(path[j],path[index]);
}
}
}
}
path[v][1] = g->V[v];
for(i = 0;i < g->Vexnum;i++)
{
for(j = 0;j < strlen(path[i]);j++)
{
printf("%c->",path[i][j]);
}
printf("\b\b:%d\n",dist[i]);
}
}
int main(int argc,char *argv[])
{
Graph *g = CreateGraph();
PrintGraph(g);
DFSVisit(g);
Dijkstra(g,0);
return 0;
}
图的乌帮图输入输出结果:
7
abcdefg
ab15
ac2
ad12
be6
cf4
dg3
fg10
fd5
gb4
###
a b c d e f g
* 15 2 12 * * *
* * * * 6 * *
* * * * * 4 *
* * * * * * 3
* * * * * * *
* * * 5 * * 10
* 4 * * * * *
abecfdg
a->a:65535
a->b:15
a->c:2
a->c->f->d:11
a->b->e:21
a->c->f:6
a->c->f->d->g:14(这个输出结果是没有增加那两条边的结果)
7
abcdefg
ab15
ac2
ad12
be6
cf4
dg3
fg10
fd5
gb4
ce8
ef9
###
a b c d e f g
* 15 2 12 * * *
* * * * 6 * *
* * * * 8 4 *
* * * * * * 3
* * * * * 9 *
* * * 5 * * 10
* 4 * * * * *
abefdgc
a->a:65535
a->b:15
a->c:2
a->c->f->d:11
a->c->e:10
a->c->f:6
a->c->f->d->g:14(增加了两条边长的输入输出结果)
图的第二种遍历(广度优先搜素,不加ce、ef的值)
#include<stdio.h>
#include<stdlib.h>
#define MAX 100
#define VERYBIG 65535
typedef char Vtype;//顶点的数据类型
typedef int Adjtype;//邻接矩阵的类型,其值就是边的权值的类型
typedef struct
{
Vtype V[MAX];//顶点的集合
Adjtype A[MAX][MAX];//边的集合,二维数组表示
int Vexnum;//顶点的个数
int Adjnum;//边的个数
}Graph;
typedef struct node
{
int num;
struct node *next;
}Node;
typedef struct list
{
Node *first;
Node *last;
int count;
}List;
void Ruidui(List *l,int ch)
{
if(l == NULL)
return ;
Node *p = (Node *)malloc(sizeof(Node));
p->num = ch;
p->next = NULL;
if(l->first == NULL)
{
l->first = l->last = p;
}
else
{
l->last->next = p;
l->last = p;
}
l->count++;
}
Node *Chudui(List *l)
{
if(l == NULL)
return NULL;
Node *p = l->first;
if(l->first == l->last)
{
l->first = l->last = NULL;
}
else
{
l->first = p->next;
p->next = NULL;
}
l->count--;
return p;
}
List * Chuangjian(void)
{
List *l = (List*)malloc(sizeof(List));
l->first = l->last = NULL;
l->count = 0;
return l;
}
int visited[MAX];
int my_index(Graph *g,char ch)
{
int i;
for(i = 0;i < g->Vexnum;i++)
{
if(g->V[i] == ch)
return i;
}
return -1;
}
int First(Graph *g, int row)
{
int i;
for(i = 0;i < g->Vexnum;i++)
{
if(g->A[row][i] != VERYBIG)
{
return i;
}
}
return -1;
}
int Next(Graph *g,int row,int column)
{
int i;
for(i = column+1;i < g->Vexnum;i++)
{
if(g->A[row][i] != VERYBIG)
{
return i;
}
}
return -1;
}
void PrintGraph(Graph *g)
{
int i,j;
for(i = 0;i <g->Vexnum;i++)
printf("%c\t",g->V[i]);
printf("\n");
for(i = 0;i < g->Vexnum;i++)
{
for(j = 0;j < g->Vexnum;j++)
{
if(g->A[i][j] == VERYBIG)
{
printf("%c\t",42);
}
else
{
printf("%d\t",g->A[i][j]);
}
}
printf("\n");
}
}
Graph *CreateGraph()
{
int i,j;
Graph *g = (Graph *)malloc(sizeof(*g));
g->Vexnum = g->Adjnum = 0;
scanf("%d",&g->Vexnum);
getchar();
for(i = 0;i < g->Vexnum;i++)
scanf("%c",&g->V[i]);
for(i = 0;i < g->Vexnum;i++)
{
for(j = 0;j < g->Vexnum;j++)
{
g->A[i][j] = VERYBIG;
}
}
Vtype from,to;
Adjtype weight;
while(1)
{
getchar();
scanf("%c%c%d",&from,&to,&weight);
if(from == '#')
break;
i = my_index(g,from);
j = my_index(g,to);
if(i != -1 && j != -1)
{
g->A[i][j] = weight;
g->Adjnum++;
}
}
return g;
}
int k=1,x=-1;
void DFS(List* l,Graph *g,int n)
{
int i;
for(i = First(g,n); i != -1;i = Next(g,n,i))
{
if(visited[i] == 0)
{
while(k>0)
{
x=i;
k=0;
}
visited[i]=1;
Ruidui(l,i);
}
}
}
void Jincheng(List *l,Graph *g)
{
Node *p=NULL;
int i;
for(i=0;i<g->Vexnum;i++)
{
visited[i]=0;
}
Ruidui(l,0);
visited[0]=1;
while(l->count > 0)
{
p=Chudui(l);
i=p->num;
if(x == i)
{
printf("\n");
k=1;
}
printf("%c\t",g->V[i]);
DFS(l,g,i);
}
for(i = 0;i < g->Vexnum;i++)
{
if(visited[i] == 0)
{
printf("%c\t",g->V[i]);
}
}
printf("\n");
}
int main(int argc,char *argv[])
{
List *l = Chuangjian();
Graph *g = CreateGraph();
PrintGraph(g);
Jincheng(l,g);
return 0;
}
输入输出:
7
abcdefg
ab15
ac2
ad12
be6
cf4
dg3
fg10
fd5
gb4
###
a b c d e f g
* 15 2 12 * * *
* * * * 6 * *
* * * * * 4 *
* * * * * * 3
* * * * * * *
* * * 5 * * 10
* 4 * * * * *
a
b c d
e f g数据