数据结构-图

图(Graph)是由顶点的有穷非空集合和顶点之间边的集合组成,通常表示为G(V, E),其中,G表示一个图,V是图G中顶点的集合,E是图G中边的集合。
ps:

  1. 图中的数据元素称为顶点(Vertex)
  2. 在图中,不允许没有顶点。定义中强调了V有穷非空
  3. 图中,任意两个顶点之间都可能有关系,顶点之间的逻辑关系用边来表示,边集可以是空的

各种图的定义

  1. (1) 无向边:若顶点vi 到vj 之间的边没有方向,则称这条边为无向边(Edge),用无序偶对(vi, vj) 来表示。
    (2)无向图:若图中 任意 两个顶点之间的边都是无向边,则称改图为无向图(undirected graphs)。
    无向图 G1 = (V1,{E1}),顶点集合V1 ={A,B,C,D},边集合E1 ={(A,B), (B,C), (C,D), (D,A)}
    (3)无向完全图:在无向图中,如果任意两个顶点之间存在边,则称该图为无向完全图。n个顶点的无向完全图有n*(n - 1)/2条边。

  2. (1) 有向边:若从顶点vi 到vj 的边有方向,则称该条边为有向边,也称为弧(Arc),用有序偶对(vi,vj) 来表示。vi 称为弧尾,vj 称为弧头,由A指向B的有向边就是弧,A是弧尾,B是弧头,<A,B>表示弧,注意不能写成<B,A>。
    (2)有向图:如果图中 任意 两个顶点之间的边都是有向边,则称该图为有向图。
    有向图 G2 = (V2,{E2}),顶点集合V2 ={A,B,C,D},边集合E2 ={<A,B>, <B,C>, <C,D>, <D,A>}
    (3)有向完全图:在有向图中,如果任意两个顶点之间 存在方向相反的两条弧 ,则称该图为有向完全图。n个顶点的有向完全图有n*(n - 1)条边。

注意:无向边用小括号“()”表示,有向边用尖括号“<>”表示。

  1. 在图中,若不存在顶点到其自身的边,且同一条边不重复出现,则称这样的图为简单图。 (下图即不是简单图)
    在这里插入图片描述

  2. 有很少条边或弧的图称为稀疏图,反之称为稠密图。

  3. 有些图的边或弧具有与他相关的数字,这种与图的边或弧相关的数叫做权(Weight)。这种带权的图通常称为网(Netwrok)。

  4. 假设有两个图G=(V,{E})和G’=(v’,{E’}),如果V’是V的子集且E’是E的子集,则称G’为G的子图(Subgraph)。

图的顶点与边间的关系

  1. 对于无向图G=(V,{E}),如果(v, v’)属于E,则称顶点v和v互为邻接点(Adjacent),即v和v’相邻接。边(v, v’)依附(incident)于v和v‘,或者说(v, v’)与顶点v和v’相关联。顶点v的度(Degree)是和v相关联的边的数目,记为TD(v)。并且边数其实就是各顶点度数和的一半,简记为:e=1/2*∑nTD(vi)。
  2. 对于有向图G=(V, {E}),如果弧<v, v’>∈E,则称顶点v邻接 顶点v’,顶点v’邻接自 顶点v。弧<v, v’>和顶点v, v’相关联。以顶点v为头的弧的数目称为v的入度(InDegree),记为ID(v);以v为尾的弧的数目称为v的出度(OutDegree),记为OD(v);顶点v的度为TD(v) = ID(v) + OD(v)。有向图的度即各顶点的出度和或入度和。
  3. 无向图G=(V,{E})中从顶点v到顶点v’的路径(Path)是一个顶点序列。如果G是有向图,则路径也是有向的(所经顶点都必须在顶点集合中)。在树中根结点到任意结点路径是唯一的,但是图中顶点与顶点之间的路径确实不唯一的。
  4. 路径上的长度是路径上的边或弧的数目。
  5. 第一个顶点和最后一个顶点相同的路径称为回路或环(Cycle)。序列中顶点不重复出现的路径称为简单路径。除了第一个顶点和最后一个顶点之外,其余顶点不重复出现的回路,称为简单回路或简单环。(下图,左边是简单环,右边不是简单环)
    在这里插入图片描述

连通图及其相关术语

在无向图G中,如果从顶点v到顶点v’有路径,则称v和v’是连通的。如果对于图中任意两个顶点vi 、vj ∈V,vi 和vj 都是连通的,则称G是连通图(Connected Graph)。
无向图中的极大连通子图称为连通分量。它强调:

  1. 要是连通子图;
  2. 子图要是连通的;
  3. 连通子图含有极大顶点数;
  4. 具有极大顶点数的连通子图包含依附于这些顶点的所有边。

在有向图G中,如果对于每一对vi 、vj 、vi ≠vj ,从vi 到vj 和从vj 到vi 都存在路径,则称G是强连通图。有向图中的极大强连通子图称做有向图的强连通分量。

连通图的生成树

所谓一个连通图的生成树是一个极小的连通子图,它含有图中的全部的n个顶点,但只有足以构成一棵树的n-1条边。下图中,1为普通图,删除两条边后的2,3,即为生成树,不过满足定义的n-1条边的图并不一定是生成树如4。由此引申出,如果一个图有n个顶点和小于n-1条边,则是非连通图,如果他的边数多于n-1条边,则必定构成一个环(连通图)

在这里插入图片描述
如果一个有向图恰有一个顶点的入度为0,其余顶点的入度均为1,则是一个有向树。其所谓入度为0其实就相当于树中的根结点,其余顶点的入度为1则是树种除根结点以外的结点只有一个双亲。

一个有向图的生成森林由若干棵有向树组成,含有图中的全部顶点,但只有足以构成若干棵不相交的有向树的弧。
(下图:2、3为1有向图的生成森林)
在这里插入图片描述

总结

  1. 图按照有无方向分为有向图和无向图。有向图由顶点和弧构成,无向图由顶点和边构成。弧由弧头和弧尾之分。
  2. 图按照边或弧的多少分为稠密图和稀疏图。如果任意两个顶点之间都存在边叫完全图,有向的叫有向完全图。若无重复的边或顶点到自身的边则叫简单图
  3. 图中的顶点由邻接点、依附的概念。无向图的顶点的边数叫做度,有向图的顶点有入度和出度。
  4. 图上的边或权带数值则成为
  5. 图中顶点之间存在路径,两顶点存在路径则说明是连通的,如果路径最终回到起始点则称为,当中不重复叫简单路径。若任意两顶点都是连通的,则图就是连通图,有向的则称强连通图
  6. 图中有子图,若子图极大连通就是连通分量,有向的则是强连通分量。
  7. 无向图中联通且n个顶点n-1条边叫生成树。有向图中一顶点入度为0其余顶点入度为1的叫有向树。一个有向图有若干棵有向树构成生成森林

图的储存结构

图不可能用简单的顺序储存结构来表示。而多重链表的方式,尽管可以实现图结构,但是由于各顶点的度数相差很大,会导致空间的浪费或者操作不便。下为五种不同的储存结构。

邻接矩阵

图的邻接矩阵(Adjacency Matrix)储存方式使用两个数组来表示图。一个一维数组储存图中的顶点信息,一个二维数组(称为邻接矩阵)储存图中的边或弧的信息。顶点数组为vertex[4] = {},边数组arc[4][4]={}。

  1. 在无向图中,矩阵的主对角线的值全为0,因为不存在顶点到自身的边,并且由于是无向图,v1 到v2 的边存在即v2 到v1 的边存在,所以无向图的边数组是一个对称矩阵。
    有了这个矩阵,很容易可以得知图中的信息:
    (1)可以轻易判断两顶点是否有边
    (2)某个顶点的度,就是这个顶点vi 在邻接矩阵中第i行(或第i列)的元素之和
    (3)顶点vi 的所有邻接点就是将矩阵中第i行元素扫描一遍,a[i][j]为1就是邻接点。

  2. 在有向图中,邻接矩阵并不对成。
    (1)有向图讲究入度和出度,顶点v1 的入度为第v1 列各数之和,顶点v1 的出度为第v1 行的各数之和
    (2)判断顶点vi 到vj 是否存在弧,只需要查找arc[i][j]是否为1即可。。

  3. 在图中,每条边上带有权值的图叫做网。我们用wij 表示(vi , vj )或<vi, vj>上的权值。
    (1)在网的邻接矩阵中,用∞表示两点不相连,因为权值wij 大多数情况下是正值,但是也有可能是0,或者为负值,因此要用一个不可能的值来代表不存在,即∞。
    (2)网的邻接矩阵中,主对角线上的值依旧是0.

typedef char VertexType;//顶点类型
typedef int EdgeType;//边上的权值类型
#define MAXVEX 100//最大顶点数
#define INFINITY 65535//用65535代表∞

typedef struct
{
   
	char vexs[MAXVEX];//顶点表
	int arc[MAXVEX][MAXVEX];//边表
	int numNodes, numEdges;//当前图的顶点数和边数
}MGraph;
void CraeteMGraph(MGrapg *G)
{
   
	int i, j, k, w;
	printf("intput nodes and edges' nums:\n");
	scanf("%d, %d", &G->numNodes, &G->numEdges);//输入顶点数和边数

	for(i = 0; i < G->numNodes; i++)//建立顶点表
		scanf(&G->vex[i]);
	for(i = 0; i < G->numNodes; i++)//初始化邻接矩阵
		for(j = 0; j < G->numNodes; j++)
			G->arc[i][j] = INFINITY;

	for(k = 0; k < numEdges; k++)//读入numEdges条边,建立邻接矩阵
	{
   
		printf("Intput edge(vi, vj)'s 'i' , 'j' and weight");
		scanf("%d %d %d", &i, &j, &w);
		G->arc[i][j] == w;
		G->arc[j][i] == w;//无向图中,邻接矩阵关于主对角线对称
	}
}

从代码可以看出,建立完整邻接表的时间复杂度为O(n + n2 + e),其中邻接矩阵G->arc的初始化耗费了O(n2)的时间。

邻接表

邻接表的思路与树的孩子储存法相同。在图中,把数组与链表相结合的储存方式称为邻接表(Adjacency List)。 利用邻接表储存稀疏图可以避免空间浪费。
邻接表的处理方法:
(1)图中的一个顶点用一维数组储存(也可以使用单链表,但是一维数组可以较容易地读取顶点信息)。另外,对于顶点数组中,每个数据元素还需要储存指向第一个邻接点的指针,以便查找该顶点的边信息
(2)图中每一个顶点vi 的所有邻接点构成一个线性表,由于邻接点个数不定,所以用单链表储存,无向图称为顶点vi 的边表,有向图称为顶点vi 作为弧尾的出边表。

通过邻接表获取信息:
(1)要想知道某个结点的度,就去查找这个顶点的边表的结点个数
(2)判断顶点vi 和vj 是否存在边,只需要测试顶点vi 的边表中adjvex是否存在结点vj 对应的下标j即可
(3)求顶点的所有邻接点,其实就是对此顶点的边表进行遍历,得到的adjvex域对应的顶点就是邻接点
(4)有向图建立的邻接表,很容易可以得知某个结点的出度。
邻接表图
有时为了便于确定顶点的入度或以顶点为弧头的弧,我们可以建立一个有向图的拟邻接表,即对每个顶点vi 都建立一个链接为vi 为弧头的表,如上。

对于带权值的网图,在边表结点定义中再加一个weight的数据域用于储存权值信息即可。


typedef char VertexType;//顶点类型
typedef int EdgeType;//边上的权值类型

typedef struct EdgeNode//边表结点
{
   
	int adjvex;//邻接点域,储存该顶点对应的下标
	int info;//储存权值,非网图非必要
	struct EdgeNode *next;//指向下一个邻接点
}EdgeNode;

typedef struct VertexNode//顶点表结点
{
   
	char data;//顶点域,储存顶点信息
	EdgeNode *firstedge;//边表头指针
}VertexNode, AdjList[MAXVEX];

typedef struct
{
   
	AdjList adjList;
	int numNodes, numEdges;/当前图中的顶点数和边数
}GraphAdjList;
void CreateALGrapg(GraphAdjList *G)
{
   
	int i, j, k;
	EdgeNode *e;
	printf("Intput vertex and edge's number:\n");
	scanf("%d %d", &G->numNodes, &G->numEdges);//顶点数和边数

	for(i = 0; i < G->numNodes; i++)//建立顶点表
	{
   
		scanf(&G->adjList[i].data);//输入顶点信息
		G->adjList[i].firstedge = NULL;//将边表置空
	}

	for(k = 0; k < G->numEdges; k++)//建立边表
	{
   
		printf("Intput edge's vertex number:\n");
		scanf("%d %d",&i, &j);//输入边的序号信息

		//即头插法
		e = (*EdgeNode *)malloc(sizeof(EdgeNode));//给边表结点申请空间
		e->adjvex = j;//邻接序号
		e->next = G->adjList[i].firstedge;//将e指针指向当前顶点指向的结点
		G->adjList[i].firstedge = e;//将当前结点的指针指向e
		e = (EdgeNode *)malloc
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值