集万千宠爱于一身的“图”

在这里插入图片描述

基本术语

(1)无向完全图和有向完全图无向完全图有n(n-1)/2条边;有向完全图有n(n-1)条边。
(2)权和网:带权的图称为网。
(3)度,入度和出度度=入度+出度
(4)路径和路径长度:路径长度是一条路径上经过的边或弧的数目。
(5)连通,连通图和连接分量若无向图中任意两顶点都是连通的,则为连通图。连通分量指的是无向图中的极大连通子图。
( G2为连通图;G3为非连通图 )
在这里插入图片描述

在这里插入图片描述

(6)强连接图和强连接分量若有向图中任意两顶点都是连通的,则为强连通图。有向图中的极大强连通子图称为有向图的强连通分量。
( G1不是强连通图,但其有两个强连通分量。)
在这里插入图片描述
在这里插入图片描述

(7)连接图的生成树若一个极小连通子图含有图中全部顶点,但只有足以构成一棵树的 n-1 条边,则为连通图的生成树。一棵有 n 个顶点的生成树有且仅有 n-1 条边。如果一个图有 n 个顶点和小于 n-1 条边,则是非连通图;多于 n-1 条边,则一定是环。但有 n-1 条边的图不一定是生成树。

在这里插入图片描述
在这里插入图片描述
(如果在一棵生成树上添加一条边,必定构成一个环,因为这条边使得它依附的那两个顶点之间有了第二条路径。)

(8)有向树和生成森林有一个顶点的入度为 0 ,其余顶点的入度均为 1 的有向图称为有向树。一个有向图的生成森林是由若干棵有向树组成,含有图中全部顶点,但只有足以构成若干棵不相交的有向树的弧。

在这里插入图片描述

类型定义

数据对象: V 为顶点集,是具有相同特性的数据元素的集合。
数据关系: R = { VR }
VR = { < v , w > | v , w 属于 V 且 P ( v , w ) < v , w > 表示从 v 到 w 的弧,谓词 P ( v , w ) 定义了弧 < v , w > 的意义或信息 }

基本操作:

  • CreateGraph ( &G , V , VR )
    按照 V 和 VR 的定义构造图 GV 是图的顶点集,VR 是图中弧的集合
  • DestroyGraph ( &G )
    销毁图 G
  • LocateVex ( G ,u )
    若 G 中存在顶点 u ,则返回该顶点在图中的位置;否则返回其他信息
    ( u 和 G 中顶点有相同特征 )
  • GetVex ( G , v )
    返回 v 的值
  • PutVex ( &G , v , value )
    对 v 赋值 value
  • FirstAdjVex ( G , v)
    返回 v 的第一个邻接顶点。若 v 在 G 中没有邻接顶点,则返回“空”
  • NextAdjVex ( G , v , w )
    返回 v 的(相对于 w 的)下一个邻接顶点。若 w 是 v 的最后一个邻接点,则返回“空”
    ( w 是 v 的邻接顶点 )
  • InsetVex ( &G , v )
    添加新顶点 v
    ( v 和图中顶点有相同特征 )
  • DeleteVex ( &G , v )
    删除 G 中顶点 v 及其相关的弧
  • InsertArc ( &G , v , w )
    增添弧 < v , w >,若 G 为无向图,则还增添对称弧 < w , v >
    ( v 和 w 为 G 中两个顶点 )
  • DeleteArc ( &G , v , w )
    删除弧 < v , w >,若 G 为无向图,则还删除对称弧 < w , v >
  • DFSTraverse ( G )
    对图进行深度优先遍历,在遍历过程中对每个顶点访问一次
  • BFSTraverse ( G )
    对图进行广度优先遍历,在遍历过程中对每个顶点访问一次

存储结构

邻接矩阵

邻接矩阵是表示顶点之间相邻关系的矩阵。

邻接矩阵表示法

在这里插入图片描述
在这里插入图片描述

举例:
在这里插入图片描述

图的邻接矩阵存储表示

#define MAXInt 32767 //表示极大值,即最大值
#define MVNum 100  //最大顶点数
typedef char VerTexType;  //假设顶点的数据类型为字符型
typedef int ArcType;  //假设边的权值类型为整型
typedef struct
{
  VerTexType vexs [MVNum];  //顶点表
  ArcType arcs [MVNum] [MVNum];  //邻接矩阵
  int vexnum,arcnum;  //图的当前点数和边数
 }AMGraph;

邻接矩阵表示法创建无向网

已知一个图的点和边,使用邻接矩阵表示法来创建此图的方法比较简单。

步骤

(1)输入总顶点数和总边数
(2)依次输入点的信息存入顶点表中
(3)初始化邻接矩阵,使每个权值初始化为极大值
(4)构造邻接矩阵。依次输入每条边依附的顶点和其权值,确定两个顶点在图中的位置之后,使相应边赋予相应的权值,同时使其对称边赋予相同的权值。

算法
Status CreateUDN(AMGraph &G)
{  //邻接矩阵表示法创建无向网 G
  cin>>G.vexnum>>G.arcnum;  //输入总顶点数,总边数
  for(i=0;i<G.vexnum;++i)  //依次输入点的信息
   {
     cin>>G.vexs[i];
   }
   for(i=0;i<G.vexnum;++i)  //初始化邻接矩阵,边的权值均置为极大值 MaxInt
   {
     for(j=0;j<G.vexnum;++j)
     {
       G.arcs[i][j]=MaxInt;
     }
   }
   for(k=0;k<G.arcnum;++k)  //构造邻接矩阵
   {
     cin>>v1>>v2>>w;  //输入一条边依附的顶点及权值
     i=LocateVex(G,v1);
     j=LocateVex(G,v2);  //确定 v1 和 v2 在 G 中的位置,即顶点数组的下标
     G.arcs[i][j]=w;  //边 < v1 , v2 > 的权值置为 w
     G.arcs[j][i]=G.arcs[i][j];  //置 < v1 , v2 > 的对称边 < v2 , v1 > 的权值为 w
   }  //for
   return OK;
}

分析
  • 时间复杂度为 O(n^2)
    若创建无向图,则需将算法改动两处。(1)初始化邻接矩阵时将边的权值均初始化为 0 ;(2)构造邻接矩阵时将权值 w 改为常量值 1 。也可经过修改建立一个有向网或有向图。

邻接矩阵表示法优缺点

  • 优点
    (1)便于判断两个顶点之间是否有边,即根据 A [ i ][ j ]=0 或 1 来判断
    (2)便于计算各个顶点的度。对于无向图,邻接矩阵第 i 行元素之和就是顶点 i 的度;对于有向图,第 i 行元素之和就是顶点 i 的出度,第 i 列元素之和就是顶点 i 的入度。

  • 缺点
    (1)不便于增加和删除顶点
    (2)不便于统计边的数目,需要扫描邻接矩阵所有元素才能统计完毕,时间复杂度为 O(n^2)
    (3)空间复杂度高。如果是有向图, n 个顶点需要 n^2 个单元存储边。如果是无向图,因其邻接矩阵是对称的,所以对规模较大的邻接矩阵可以采用存储压缩的方法,仅存储下三角(或上三角)的元素,这样需要 n(n-1)/2 个单元即可。但无论以何种方式存储,邻接矩阵表示法的空间复杂度均为 O(n^2) ,这对于稀疏图而言尤其浪费空间

邻接表

邻接表表示法

邻接表是图的一种链式存储结构

邻接表的两部分:

  • 表头结点表

由所有表头结点以顺序结构的形式存储,以便可以随机访问任一顶点的边链表。

由数据域 (data) 和链域 (firstarc)两部分构成:
在这里插入图片描述

  • 边表

由表示图中顶点间关系的 2n 个边链表组成。

由邻接点域 (adjvex) 、数据域 (info) 和链域 (nextarc) 三部分构成:

在这里插入图片描述

图的邻接表存储表示

#define MVNum 100  //最大定点数
typedef struct ArcNode  //边结点
{
  int adjvex;  //该边所指向的顶点的位置
  struct ArcNode * nextarc;  //指向下一条边的指针
  OtherInfo info;  //和边相关的信息(如权值等)
}ArcNode;
typedef struct VNode  //顶点信息
{
  VerTexType data;
  ArcNode *firstarc;  //指向第一条依附该顶点的边的指针
}VNode,AdjList[MVNum];  //Adjlist表示邻接表类型
typedef struct  //邻接表
{
  AdjList vertices;
  int vexnum,arcnum;  //图的当前顶点数和边数
}ALGraph;

邻接表表示法创建无向图

基于上述的邻接表表示法,要创建一个图则需要创建其相应的顶点表和边表

步骤

(1)输入总顶点数和总边数
(2)依次输入点的信息存入顶点表中,使每个表头结点的指针域初始化为 NULL
(3)创建邻接表。依次输入每条边依附的两个顶点,确定这两个顶点的序号 i 和 j 之后,将此结点分别插入 vi 和 vj 对应的两个边链表的头部

算法
Status CreateUDG(ALGraph &G)
{  //邻接表表示法创建无向图 G
  cin>>G.vexnum>>G.arcnum;  //输入总顶点数,总边数 
  for(i=0;i<G.vexnum;++i)  //输入各点,构造表头结点表
  {
    cin>>G.vertices[i].data;  //输入顶点值
    G.vertices[i].firstarc=NULL;  //初始化表头结点的指针域为NULL
  }  //for
  for(k=0;k<G.arcnum;++k)  //输入各边,构造邻接表
  {
    cin>>v1>>v2;  //输入一条边依附的两个顶点
    i=LocateVex(G,v1);
    j=LocateVex(G,v2);  //确定 v1 和 v2 在 G 中位置,即顶点在 G.vertices中的序号
    p1=new ArcNode;  //生成一个新的边结点 *p1
    p1->adjvex=j;  //邻接点序号为 j
    p1->nextarc=G.vertices[i].firstarc;
    G.vertices[i].firstarc=p1;  //将新结点 *p1 插入顶点 vi 的边表头部
    p2=new ArcNode;  //生成另一个对称的新的边结点 *p2
    p2->adjvex=i;  //邻接表序号为 i
    p2->nextarc=G.vertices[j].firstrrc;
    G.vertices[j].firstarc=p2;  //将新结点 *p2 插入顶点 vj 的边表头部
  }  //for
  return OK;
}
分析
  • 时间复杂度为 O(n+e)
    建立有向图的邻接表与此类似,只是更加简单,每读入一个顶点对序号 < i , j > ,仅需生成一个邻接点序号为 j 的边表结点,并将其插入到 vi 的边链表头部即可。若要创建网的邻接表,可以将边的权值存储在 info 域中

邻接表表示法优缺点

  • 优点
    (1)便于增加和删除顶点
    (2)便于统计边的数目,按顶点表顺序扫描所有边表可得到边的数目,时间复杂度为 O(n+e)
    (3)空间效率高。对于一个具有 n 个顶点 e 条边的图 G,若 G 是无向图,则在其邻接表表示中有 n 个顶点表结点和 2e 个边表结点;若 G 是有向图,则在它的邻接表表示或逆邻接表表示中均有 n 个顶点表和 e 个边表结点。所以,邻接表或逆邻接表表示的空间复杂度为 O(n+e),适合表示稀疏图。对于稠密图,考虑到邻接表中要附加链域,因此常采用邻接矩阵表示法。
  • 缺点
    (1)不方便判断顶点之间是否有边,要判定 vi 和 vj 之间是否有边,就需扫描第 i 个边表,最坏情况下要耗费 O(n) 时间
    (2)不便于计算有向图各个顶点的度。对于无向图,在邻接表表示中顶点 vi 的度是第 i 个边表中的结点个数。在有向图的邻接表中,第 i 个边表上的结点个数是顶点 vi 的出度,但求 vi 的入度比较困难,需遍历各顶点的边表。若有向图采用逆邻接表表示,则与邻接表表示相反,求顶点的入度容易,而求顶点的出度较难
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值