数据结构-图(上)

202 阅读8分钟

6.1 图的基本概念

  • 图的定义

图:图G由顶点集V和边集E组成,记为G = (V, E),其中V(G)表示图G中顶点的有限非空集;E(G) 表示图G中顶点之间的关系(边)集合。若V = {v1, v2, … , vn},则用|V|表示图G中顶点的个 数,也称图G的阶,,用|E|表示图G中边的条数。

  • 注意:线性表可以是空表,树可以是空树,但图不可以是空,即V一定是非空集

  • 无向图:若E是无向边(简称边)的有限集合时,则图G为无向图。边是顶点的无序对,记为(v, w)或(w, v),因为(v, w) = (w, v),其 中v、w是顶点。可以说顶点w和顶点v互为邻接点。边(v, w) 依附于顶点w和v,或者说边(v, w)和顶点v、w相关联

  • 有向图:若E是有向边(也称弧)的有限集合时,则图G为有向图。 弧是顶点的有序对,记为<v,w>,其中v、w是顶点,v称为弧尾,w称为弧头,<v,w>称为从顶点v到顶点w的弧,也称 v邻接到w,或w邻接自v。<v,w> ≠<w,v>

  • 简单图

① 不存在重复边;

② 不存在顶点到自身的边 (数据结构课程只探讨 “简单图”)

  • 多重图

图G中某两个结点之间的边数多于一条,又允许顶点通过同一条边和自己关联

  • 无向图:顶点v的度是指依附于该顶点的边的条数,记为TD(v)。

在具有n个顶点、e条边的无向图中, 即无向图的全部顶点的度的和等于边数的2倍

  • 有向图:入度是以顶点v为终点的有向边的数目,记为ID(v);

出度是以顶点v为起点的有向边的数目,记为OD(v)。

顶点v的度等于其入度和出度之和,即TD(v) = ID(v) + OD(v)。

在具有n个顶点、e条边的有向图中,,即入度和出度的数量相等且等于e

  • 顶点的关系描述

路径——顶点vp到顶点vq之间的一条路径是指顶点序列,

回路——第一个顶点和最后一个顶点相同的路径称为回路或环

简单路径——在路径序列中,顶点不重复出现的路径称为简单路径。

简单回路——除第一个顶点和最后一个顶点外,其余顶点不重复出现的回路称为简单回路。

路径长度——路径上边的数目

点到点的距离——从顶点u出发到顶点v的最短路径若存在,则此路径的长度称为从u到v的距离。 若从u到v根本 不存在路径,则记该距离为无穷(∞)。

无向图中,若从顶点v到顶点w有路径存在,则称v和w是连通的

有向图中,若从顶点v到顶点w和从顶点w到顶点v之间都有路径,则称这两个顶点是强连通的

图G中任意两个顶点都是连通的,则称图G为连通图,否则称为非连通图。

若图中任何一对顶点都是强连通的,则称此图为强连通图。

  • 研究图的局部—子图、生成子图

设有两个图G = (V, E)和G ′ = (V ′ , E ′ ),若V ′ 是V的子集,且 E ′ 是 E的子集,则称G ′ 是G的子图

若有满足V(G ′ ) = V(G)的子图G ′ ,则称其为G的生成子图

有向图的子图和生成子图也是一样的

无向图中的极大连通子图称为连通分量 子图必须连通,且包含尽可能多的顶点和边

有向图中的极大强连通子图称为有向图的强连通分量 子图必须强连通,同时 保留尽可能多的边

  • 生成树:连通图的生成树是包含图中全部顶点的一个极小连通子图。

若图中顶点数为n,则它的生成树含有 n-1 条边。对生成树而言,若砍去它的一条边,则会变成非连通 图,若加上一条边则会形成一个回路。(因此边要尽可能的少,但要保持连通)

  • 生成森林:在非连通图中,连通分量的生成树构成了非连通图的生成森林

  • 边的权、带权图/网

边的权——在一个图中,每条边都可以标上具有某种含义的数值,该数值称为该边的权值。

带权图/网——边上带有权值的图称为带权图,也称网。

带权路径长度——当图是带权图时,一条路径上所有边的权值之和,称为该路径的带权路径长度

  • 特殊形态的图

稀疏图:边数很少的图称为稀疏图 反之称为稠密图

树——不存在回路,且连通的无向图

n个顶点的树,必有n-1条边。

有向树——一个顶点的入度为0、其余顶点的 入度均为1的有向图,称为有向树

6.2 图的存储及基本操作

6.2.1 邻接矩阵法
  • 邻接矩阵存储无向图、有向图
#define MaxVertexNum 100	//顶点数目的最大值
 
typedef struct{
    char Vex[MaxVertexNum];		//顶点表
    int Edge[MaxVertexNum][MaxVertexNum];	//邻接矩阵,边表
    int vexnum,arcnum;			//图的当前顶点数和边数
}MGraph;

第i个结点的度 = 第i行(或第i列)的非零元素个数

第i个结点的出度 = 第i行的非零元素个数

第i个结点的入度 = 第i列的非零元素个数

第i个结点的度 = 第i行、第i列的非零元素个数之和

邻接矩阵法求顶点的度/出度/入度的时间复杂度为O(|V|)

#define MaxVertexNum 100		//顶点数目的最大值
#define INFINITY 2147483647;	//表示“无穷”
 
typedef char VertexType;	//顶点数据类型
typedef int EdgeType;		//边数据类型
 
typedef struct{
    VertexType Vex[MaxVertexNum];	//顶点表
    EdgeType Edge[MaxVertexNum][MaxVertexNum];	//边的权值
    int vexnum,arcnum;		//图的当前顶点数和弧数
}MGraph;
  • 邻接矩阵法的性能分析

空间复杂度:O(|V|^2) ——只和顶点数相关,和实际的边数无关

适合用于存储稠密图

无向图的邻接矩阵是对称矩阵,可以压缩存储(只存储上三角区/下三角区)

6.2.2 邻接表法
  • 邻接表法(顺序+链式存储)
#define MVNum 100							//最大顶点数
 
typedef struct ArcNode{                		//边/弧 
    int adjvex;                             //邻接点的位置 
    struct ArcNode *next;	      			//指向下一个表结点的指针 
}ArcNode;
 
typedef struct VNode{ 
    char data;                    	        //顶点信息 
    ArcNode *first;         				//第一条边/弧 
}VNode, AdjList[MVNum];                 	//AdjList表示邻接表类型 
 
typedef struct{ 
    AdjList vertices;              			//头结点数组
    int vexnum, arcnum;     				//当前的顶点数和边数 
}ALGraph; 

邻接表 邻接矩阵

空间复杂度 无向图 O(|V| + 2|E|) ;有向图O(|V| + |E|) O(|V|^2

适合用于 存储稀疏图 存储稠密图

表示方式 不唯一 唯一

计算度/出度/入度 计算有向图的度、入度不方便,其余很方便

必须遍历对应行或列

找相邻的边

找有向图的入边不方便,其余很方便 必须遍历对应行或列

6.2.3 十字链表
  • 十字链表存储有向图
#define MAX_VERTEX_NUM 20	//最大顶点数量
 
typedef struct ArcBox{		//弧结点
	int tailvex, headvex;	//弧尾,弧头顶点编号(一维数组下标)
	struct ArcBox *hlink, *tlink;	//弧头相同、弧尾相同的下一条弧的链域
	InfoType info;			//权值
}ArcBox;
 
typedef struct VexNode{		//顶点结点
	VertexType data;		//顶点数据域
	ArcBox *firstin, *firstout;	//该顶点的第一条入弧和第一条出弧
}VexNode;
 
typedef struct{				//有向图
	VexNode xlist[MAX_VERTEX_NUM];	//存储顶点的一维数组
	int vexnum, arcnum;	//有向图的当前顶点数和弧数
}OLGraph;
  • 十字链表法性能分析

空间复杂度:O(|V|+|E|)

顺着绿色线路找可以找到指定顶点的所有出边

顺着橙色线路找可以找到指定顶点的所有入边

注意:十字链表只用于存储有向图

6.2.4 邻接多重表
#define MAX_VERTEX_NUM 20	//最大顶点数量
 
struct EBox{				//边结点
	int i,j; 				//该边依附的两个顶点的位置(一维数组下标)
	EBox *ilink,*jlink; 	//分别指向依附这两个顶点的下一条边
	InfoType info; 			//边的权值
};
struct VexBox{
	VertexType data;
	EBox *firstedge; 		//指向第一条依附该顶点的边
};
struct AMLGraph{
	VexBox adjmulist[MAX_VERTEX_NUM];
	int vexnum,edgenum; 	//无向图的当前顶点数和边数
};
  • 空间复杂度:O(|V|+|E|)

删除边、删除节点等操 作很方便

注意:邻接多重表只适 用于存储无向图

6.2.5 图的基本操作

Adjacent(G,x,y):判断图G是否存在边<x, y>或(x, y)。(<>表示有向图,()表示无向图)

Neighbors(G,x):列出图G中与结点x邻接的边。

lnsertVertex(G,x):在图G中插入顶点x。

DeleteVertex(G,x):从图G中删除顶点x。

AddEdge(G,x,y):若无向边(x,y)或有向边<x, y>不存在,则向图G中添加该边。RemoveEdge(G,x,y):若无向 边(x, y)或有向边<x, y>存在,则从图G中删除该边。

FirstNeighbor(G,x):求图G中顶点x的第一个邻接点,若有则返回顶点号。若x没有邻接点或图中不存在x,则返回-1。

NextNeighbor(G,x,y):假设图G中顶点y是顶点x的一个邻接点,返回除y之外顶点x的下一个邻接点的顶点号,若y是x的最后一个邻接点,则返回-1。

Get_edge_value(G,x,y):获取图G中边(x, y)或<x, y>对应的权值。

Set edge value(G,x,y,v):设置图G中边(x, y)或<x, y>对应的权值为v。