图是一种很重要的数据结构,树也是一种特殊的图。
图的表示形式:
(1) 邻接表:
所谓用邻接表表示图,是将图中的节点存放在一个顺序表中,再以每个节点开头分别构建单链表,单链表中的节点(头节点除外)存放的数据是头结点邻接(无向图)或指向(有向图)的节点在顺序表中的编号。
无向图的单链表的节点是头结点在图中所有邻接的节点;
有向图的单链表的节点是头节点在图中指向的节点在顺序表中的编号,所表示的指向关系不重复
无论是无向图还是有向图,单链表内部节点都是无序的
①-②,②-③,③-④,④-⑤,⑤-①,
图的创建:
1建立一个顺序表,按顺序获得结点中的数据:
(有n个节点重复n次,这里的节点数据类型是字符型)
for(i=0;i<n;i++)
{ printf("Input the information of the %d vertex/n",i);
scanf("%c",&c);
G[i].data=c;
}
2创建图的边:
有n个节点就循环n次,输入的是该节点指向或邻接的节点在顺序表中的编号,若输入的是结束标志则进入for的下一循环
for(i=0;i<n;i++) //有n个节点,所以循环n次,在每一次循环中创建该节点对应的邻接边
{
printf("create the edges of the %dth vertex:/n",i);
scanf("%d",&e);
while(e!=0)
{
}
}
创建节点,插入单链表内(在while循环内部编写):
创建单链表节点:
p=(ArcNode*)malloc(sizeof(ArcNode));
p->next=NULL;
p->adjvex=e;
将新创建的节点接到单链表中(由于单链表无序,为了方便,可以接到头结点后):
如果单链表中原来没有元素,直接把它接到头结点后即可:
if(G[i].firstarc==NULL)
{G[i].firstarc=p;} //如果单链表里没有邻接元素,直接把它接到顺序表对应元素上
如果单链表中有元素,将其接到头结点后面,在和后面的节点连起来:
else
{
q=G[i].firstarc; //如果单链表里有邻接元素,把它插入到顺序表对应元素的后面
G[i].firstarc=p;
p->next=q;
}
scanf("%d",&e); //只要不出现结束标志就一直输入下去
代码清单:
void createGraph(int n,VNode G[])
{ char c;
int i,e;
ArcNode *p,*q;
/*获得每一个节点的数据*/
for(i=0;i<n;i++)
{ printf("Input the information of the %d vertex/n",i);
scanf("%c",&c);
G[i].data=c;
}
/*创建图的边*/
for(i=0;i<n;i++) //有n个节点,所以循环n次,在每一次循环中创建该节点对应的邻接边
{
printf("create the edges of the %dth vertex:/n",i);
scanf("%d",&e);
while(e!=-1)
{ /*新创建一个单链表的节点*/
p=(ArcNode*)malloc(sizeof(ArcNode));
p->next=NULL;
p->adjvex=e;
/*把新创建的节点接到单链表上*/
if(G[i].firstarc==NULL)
{G[i].firstarc=p;} //如果单链表里没有邻接元素,直接把它接到顺序表对应元素上
else
{
q=G[i].firstarc; //如果单链表里有邻接元素,把它插入到顺序表对应元素的后面
G[i].firstarc=p;
p->next=q;
}
scanf("%d",&e); //只要不出现结束标志就一直输入下去
}
}
}
图的遍历:
1深度优先搜索
算法:
V0为遍历起点,访问标志数组初始值为visited[5]={0,0,0,0,0};
1访问v0,visited[0]置1,visited[5]={1,0,0,0,0};
2用函数NextAdj()得到v0的一个邻接点,如果返回值不是-1,则有邻接点,例如v1。
2.1访问顶点v1,visited[1]置1,这样visited[5]={1,1,0,0,0};
2.2用函数NextAdj()得到v1的一个邻接点,如果返回值不是-1,则有邻接点,例如v2。
2.2.1访问顶点v2,visited[2]置1,这样visited[5]={1,1,1,0,0};
2.2.2用函数NextAdj()得到v1的一个邻接点,返回-1,因为都被访问过了;
2.3用函数NextAdj()得到v1的下一个邻接点,返回-1,因为都被访问过;
3用函数NextAdj()得到v0的下一个邻接点,返回值不是-1,则有邻接点,例如v3。
3.1访问顶点v3,visited[3]置1,这样visited[5]={1,1,1,1,0};
3.2用函数NextAdj()得到v3的一个邻接点, 返回值是-1,因为都被访问过;
4用函数NextAdj()得到v0的下一个邻接点,返回值不是-1,则有邻接点,例如v4。
4.1访问顶点v4,visited[4]置1,这样visited[5]={1,1,1,1,1};
4.2用函数NextAdj()得到v4的一个邻接点, 返回值是-1,因为都被访问过;
5用函数NextAdj()得到v0的下一个邻接点,返回-1,因为都被访问过。
这是一个递归的过程,相同等级的标号为同一层的递归调用。基本思想是:
每次取一个相邻的点走下去,一直走到无路可走了再退回来,取另一个相邻的点再走下去。这称为深度优先搜索
代码实现:
递归的过程可以这样描述:
访问当前节点->获得邻接的一个顶点->递归地访问邻接的顶点->获得邻接的一个顶点->(有)递归地深度优先搜索邻接的顶点/(无)递归返回;
访问当前节点:1访问当前节点;2访问标志置1;
visit(G,v); //访问当前节点
visited[v]=1; //顶点v对应的访问标志置1
获得邻接的一个顶点
w=NextAdj(G,v,visited); //找到顶点v的下一个邻接点,如无邻接点,返回-1
(有)递归地深度优先搜索邻接的顶点/(无)递归返回;
while(w!=-1)
{
DFS(G,w,visited); //如果该顶点未被访问,则对其深度优先搜索
w=NextAdj(G,v,visited); //找到顶点v的下一个邻接点
}
为了保证遍历完全,一次递归深度优先遍历完成后要从未访问的节点出发进行深度优先遍历,直至所有节点都被访问为止;
for(i=0;i<n;i++)
{
if(visited[i]==0)
{
DFS(G,i,visited); //若有顶点没有访问,从该顶点开始深度优先搜索
}
}
获得邻接的未访问的节点(NextAdj()函数)
从顺序表第v节点开始,用while循环循着next指针一直遍历下去,直至遇到未访问的节点的编号为止,则返回编号值;若全部都遍历过或者单链表中没有元素,返回-1;
int NextAdj(VNode G[],int v,int visited[])
{
ArcNode *p;
p=G[v].firstarc;
while(p!=NULL)
{
if(visited[p->adjvex]==0)
{
p=p->next;
}
else return p->adjvex;
}
return -1;
}
代码清单:
#include<stdio.h>
#include<stdlib.h>
#define MAX_VERTEX_NUM 20 //最多有20个节点
/*定义单链表中的节点*/
typedef struct ArcNode
{
int adjvex; //该边指向的顶点在顺序表中的位置
struct ArcNode *next; //指向下一条边的指针
int *weight; //边上的权重,可省略
}ArcNode;
/*定义顺序表中的节点*/
typedef struct VNode
{
char data; //顶点中的数据类型,这里为字符型;
ArcNode *firstarc; //指向单链表的第一条边
}VNode;
VNode G[MAX_VERTEX_NUM]; /*图的存储容器*/
void createGraph(int n,VNode G[]);
void visit(VNode G[],int v);
void DFS(VNode G[],int v,int visited[]);
void Travel_DFS(VNode G[],int n,int visited[]);
int NextAdj(VNode G[],int v,int visited[]);
main()
{
int visited[MAX_VERTEX_NUM];
VNode G[MAX_VERTEX_NUM];
createGraph(MAX_VERTEX_NUM,G);
Travel_DFS(G,MAX_VERTEX_NUM,visited);
}
/*创建一个图*/
void createGraph(int n,VNode G[])
{ char c;
int i,e;
ArcNode *p,*q;
/*获得每一个节点的数据*/
for(i=0;i<n;i++)
{
printf("Input the information of the %d vertex/n",i);
scanf("%c",&c);
G[i].data=c;
}
/*创建图的边*/
for(i=0;i<n;i++) //有n个节点,所以循环n次,在每一次循环中创建该节点对应的邻接边
{
printf("create the edges of the %dth vertex:/n",i);
scanf("%d",&e);
while(e!=-1)
{ /*新创建一个单链表的节点*/
p=(ArcNode*)malloc(sizeof(ArcNode));
p->next=NULL;
p->adjvex=e;
/*把新创建的节点接到单链表上*/
if(G[i].firstarc==NULL)
{G[i].firstarc=p;} //如果单链表里没有邻接元素,直接把它接到顺序表对应元素上
else
{
q=G[i].firstarc; //如果单链表里有邻接元素,把它插入到顺序表对应元素的后面
G[i].firstarc=p;
p->next=q;
}
scanf("%d",&e); //只要不出现结束标志就一直输入下去
}
}
}
void visit(VNode G[],int v)
{
}
void DFS(VNode G[],int v,int visited[])
{
int w;
visit(G,v); //访问当前节点
visited[v]=1; //顶点v对应的访问标志置1
w=NextAdj(G,v,visited); //找到顶点v的下一个邻接点,如无邻接点,返回-1
while(w!=-1)
{
DFS(G,w,visited); //如果该顶点未被访问,则对其深度优先搜索
w=NextAdj(G,v,visited); //找到顶点v的下一个邻接点
}
}
void Travel_DFS(VNode G[],int n,int visited[])
{
int i;
for(i=0;i<n;i++)
{visited[i]=0;} //将标记数组初始化为0
for(i=0;i<n;i++)
{
if(visited[i]==0)
{
DFS(G,i,visited); //若有顶点没有访问,从该顶点开始深度优先搜索
}
}
}
int NextAdj(VNode G[],int v,int visited[])
{
ArcNode *p;
p=G[v].firstarc;
while(p!=NULL)
{
if(visited[p->adjvex]==0)
{
p=p->next;
}
else return p->adjvex;
}
return -1;
}