数据结构中的图(图的存储结构)(2016-12-30)
图是由两个集合V和E组成的,V是有限的非空顶点集,E是V上的定点对所构成的边集,分别用V(g)和E(g)来表示图中的顶点集和边集,用二元组G=(V(g),E(g))来表示图G。
图分为有向图和无向图。
有向图:<Vi,Vj>:表示从Vi开始到Vj的一条边。Vi为初始顶点或尾。Vj为头或者终端顶点。有向边称为弧。
无向图:(Vi,Vj):边是无序的,所以(Vi,Vj)和(Vj,Vi)表示同一条边。
无向完全图,有向完全图:n个点全部连结起来的的图,而有向图的俩点间有互相相反的俩条边。
强连通图,连通图,连通分量:
路径,路径长度,简单路径和简单回路:
一个顶点的度(TDi)=入度(IDi)+出度(ODi);
图的存储结构:常用的有邻接矩阵、邻接表、邻接多重表、十字链表等。
邻接矩阵表示方法:
可以分为有权图and 无权图:
有权图:aij表示<Vi,Vj>或者(Vi,Vj)的权重。若<Vi,Vj>或(Vi,Vj)不是E(g)中的边,则aij的值为0或者∞,∞表示一个计算机允许、大于所有边上权重的数,对于不存在的边,aij取0或者∞可以根据实际运算的需要或者方便而定。
无权图:
则aij的取值如下,若顶点Vi和Vj之间没有边,则取值为0,若有边则取值为1。
建立无向图邻接矩阵的算法如下:(顶点的英语为:vertex)
#define MAXSIZE 100
typedef int datatype;
typedef struct
{
datatype vexs[MAXSIZE];//顶点信息表
int edges[MAXSIZE][MAXSIZE];//邻接矩阵
int n,e;//顶点数和边数
}graph;
void Creat_graph(graph *ga)
{
int i,j,k,w;//w为每条边的权重,j,i,k都为局部变量
printf("请输入图的顶点数和边数:\n");
scanf("%d %d",&(ga->n),&(ga->e));
printf("请输入顶点信息(顶点编号),建立顶点信息表:\n");
for(i=0;i<ga->n;i++)//输入顶点信息
scanf("%c",&(ga->vexs[i]));
for(i=0;i<ga->n;i++)//邻接矩阵初始化
for(j=0;j<ga->n;j++)
ga->edges[i][j]=0;
for(k=0;k<ga->e;k++)//读入边的顶点编号和权重,建立邻接矩阵
{
printf("请输入第%d条边的顶点序号i,j和权重w:",k+1);
scanf("%d,%d",&i,&j,&w);
ga->edges[i][j]=w;
ga->edges[j][i]=w;
}
}
特点:
(1)无向图的邻接矩阵一定是对称的,而有向图的邻接矩阵不一定对称。因此用邻接矩阵表示一个具有n个定点的有向图时需要n^2个存储单元来存储邻接矩阵,对于n个顶点的无向图则需要n(n+1)/2个单元存储邻接矩阵;
(2)对于无向图,邻接矩阵的第i行(或者第i列)中非零元素的个数正好是第i个顶点的度TD(vi),对于有向图,邻接矩阵的第i行(或者第i列)非零元素的个数正好是第i个顶点的出度OD(vi)(或者入度ID(vi));
(3)用邻接矩阵很容易确定图中任意俩个顶点之间有边,但是不容易确定这个图中有多少条边。所以提出了邻接表。
算法的执行时间为O(n+n^2+e),由于e<n^2;所以算法的时间复杂度为O(n^2);
邻接表表示法:
由表头节点和表节点组成:
表头节点(data,first)
表节点:(vertexs,next);
无向图中:有n个顶点,e条边,则邻接链表需要n个头节点,2e个表节点,对于边很少的图来说,用邻接链表比用邻接矩阵更节省空间。
在无向图的邻接链表中,第i个链表中的表节点数就是定点vi的度数,对于有向图来说,第i个链表中的表节点数就是顶点vi的出度数。
表节点类型定义如下:
#define nmax 100
typedef struct node *pointer;
typedef int datatype;
struct node//表节点类型
{
int vertex;
struct node *next;
}nnode;
typedef struct //表头节点类型
{
datatype data;
pionter first;//边表头指针
}headtype;
typedef struct{//表头节点向量,即顶点表
headtype adlist[nmax];
int n,e;//顶点数及边数
}lkgraph;
如果图中有n个顶点,e条边,则邻接表需要n个表头节点,e个表节点(有向图中)或2e个表节点(无向图中),因此空间复杂度为O(n+e);
当边数较少时候,链表存储方式空间利用率较好,当变数较多时,邻接矩阵表示方法较好,如果运算中对所有的邻接点进行处理,则邻接矩阵和邻接表的时间复杂度分别为O(n^2)和O(n+e);
void creatgraph(lkgraph *ga)
{
int i,j,e,k;
pointer p;
printf("请输入顶点数:\n");
scanf("%d",&(ga->n));
for(i=1;i<=ga->n;i++)//读入顶点信息,建立顶点表
{
scanf("\n %c",&(ga->adlist[i].data));
ga->adlist[i].first=NULL;
}
e=0;//图中边的个数
scanf(“\n%d,%d\n”,&i,&j);//读入一个顶点对号i和号j
while(i>0)//顶点号大于零时候就存入
{
e++;
//头节点为i的vi的链表
p=(pointer)malloc(size(struct node));//生成新的邻接点序号为j的表节点
p->vertex=j;//p的vertex存储j节点
p->next=ga->adlist[i].first;//p->next指向NULL;
ga->adlist[i].first=p;//将新表节点插入顶点vi的边表的头部
//头节点为j的vj的链表
p=(pointer)malloc(size(structnode));//生成新的临街点序号为i的表节点
p->vertex=i;
p->next=ga->adlist[j].first;//
ga->adlist[j].first=p;//将新表节点插入到顶点vj的边表的头部
scanf("\n %d,%d\n",&i,&j);//读入一个顶点对号i和j
}
ga->e=e;
}
对上述建无向图邻接表的方法略加修改,可以得到有向图邻接表的建立方法
建立有向图的邻接表
void cr_graph(lkgraph *ga)
{
int i,j,e,k;
pointer p;
printf("请输入顶点数:\n");
scanf("%d",&(ga->n));
for(i=1;i<(ga->n);i++)//读入顶点信息,以及建立顶点表
{
scanf("\n%c",&(ga->adlist[i].data));
ga->adlist[i].first=NULL;
}
e=0;
scanf("\n %d,%d\n",&i,&j);
while(i>0)
{
e++;
p=new node;
p->vertex=j;
p->next=ga->adlist[i].first;
ga->adlist[i].first=p;
scanf("\n %d,%d\n",&i,&j);
}
ga->e=e;
}
建立无向图算法的时间复杂度为O(n+e).(n为顶点个数,e表示图中边的个数)