数据结构中的图(图的存储结构)(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("请输入图的顶点数和边数: ");

  scanf("%d %d",&(ga->n),&(ga->e));

  printf("请输入顶点信息(顶点编号),建立顶点信息表: ");

  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("请输入顶点数: ");

  scanf("%d",&(ga->n));

  for(i=1;i<=ga->n;i++)//读入顶点信息,建立顶点表

  {

    scanf(" %c",&(ga->adlist[i].data));

    ga->adlist[i].first=NULL;

  }

  e=0;//图中边的个数

  scanf(“ %d,%d ”,&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(" %d,%d ",&i,&j);//读入一个顶点对号i和j 

  }

  ga->e=e;

}

对上述建无向图邻接表的方法略加修改,可以得到有向图邻接表的建立方法

建立有向图的邻接表

void  cr_graph(lkgraph *ga)

{

  int i,j,e,k;

  pointer p;

  printf("请输入顶点数: ");

  scanf("%d",&(ga->n));

  for(i=1;i<(ga->n);i++)//读入顶点信息,以及建立顶点表

  {

    scanf(" %c",&(ga->adlist[i].data));

    ga->adlist[i].first=NULL;

  }

  e=0;

  scanf(" %d,%d ",&i,&j);

  while(i>0)

  {

    e++;

    p=new node;

    p->vertex=j;

    p->next=ga->adlist[i].first;

    ga->adlist[i].first=p;

    scanf(" %d,%d ",&i,&j);

  }

  ga->e=e;

}

建立无向图算法的时间复杂度为O(n+e).(n为顶点个数,e表示图中边的个数)

原文地址:https://www.cnblogs.com/hai5111/p/6236639.html