数据结构-图(一)

目录

图的定义

图的基本术语

图的存储结构

邻接矩阵表示法

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

邻接矩阵表示法的优缺点

邻接表表示法

邻接表表示法创建无向图

邻接表表示法的优缺点


图的定义

        图(Graph)是由顶点的有穷非空集合V(G)和顶点之间边的集合E(G)组成,通常表示为:G=(V,E)其中,G表示个图,V是图G中顶点的集合,E是图G中边的集合。若V={v1,v2,...,vn},则用∣V∣表示图G中顶点的个数,也称图G的阶,E={(u,v)∣u∈V,v∈V},用∣E∣表示图G中边的条数。E(G)可以为空集。若E(G)为空,则图G只有顶点而没有边。  

        对于图G,若边集E(G)为有向边的集合,则称该图为有向图;若边集E(G)为无向边的集合,则称该图为无向图。
        在有向图中,顶点对<x,y>是有序的,它称为从顶点x到顶点y的一条有向边。 因此,<x,y>与<y,x>是不同的两条边。顶点对用尖括号括起来,对<x,y>而言,x是有向边的始点,y是有向边的终点。<xy>也称作一条弧, 则x为弧头,y为弧尾。
        在无向图中,顶点对(x, y)是无序的,它称为与顶点x和顶点y相关联的一条边。这条边没有特定的方向,(x, y)与(y, x)是同一条边。 为了有别于有向图, 无向图的顶点对用一对圆括号括起来。

 

 

图的基本术语

        用n表示图中顶点数目,用e表示边的数目,下面介绍图结构中的一些基本术语。
        (1)子图:假设有两个图G=(v,E)和G'=(v',E'),如果v'是v的真子集且E'是E的真子集,则称G'为G的子图。下图所示为G1和G2的子图示例。


        (2)无向完全图和有向完全图:对于无向图,若具有n(n-1)/2条边, 则称为无向完全图。对于有向图,若具有n(n- 1)条弧,则称为有向完全图
        (3)稀疏图和稠密图:有很少条边或弧(如logn)的图称为稀疏图,反之称为稠密图。
        (4)权和网:在实际应用中,每条边可以标上具有某种含义的数值,该数值称为该边上的权值。这些权值可以表示从一个顶点到另一个顶点的距离或耗费。这种带权的图通常称为网。
        (5)邻接点:对于无向图G,如果图的边(v,v')属于E,则称顶点v和v'互为邻接点,即v和v'相邻接。边(v,v')依附于顶点v和v',或者说边(v,v')与顶点v和v'相关联
        (6)度、入度和出度:顶点v的是指和v相关联的边的数目,记为TD(v)。对于有向图,顶点v的度分为入度和出度。入度是以顶点v为头的弧的数目,记为ID(v);出度是以顶点v为尾的弧的数目,记为OD(v)。顶点v的度为TD(v) = ID(v) + OD(v)。一般地, 如果顶点vi的度记为TD(vi),那么一个有n个顶点,e条边的图,满足如下关系:

        (7)路径和路径长度:在无向图G中,从顶点v到顶点v'的路径是一个顶点序列
(v = v(i,0),v(i,1),…,v(i,m) = v'),其中(v(i,j-1),v(i,j))属于E,1<=j<=m。如果G是有向图,则路径也是有向的,顶点序列应满足<v(i,j-1),v(i,j)>属于E,1<=j<=m。路径长度是一条路径上经过的边或弧的数目。
        (8)回路或环:第一个顶点和最后一个顶点相同的路径称为回路或环
        (9)简单路径、简单回路或简单环:序列中顶点不重复出现的路径称为简单路径。除了第一个顶点和最后一个顶点之外,其余顶点不重复出现的回路,称为简单回路简单环
        (10)连通、连通图和连通分量:在无向图G中,如果从顶点v到顶点v'有路径,则称v和v'是连通的。如果对于图中任意两个顶点vi,vj属于V, vi和vj都是连通的,则称G是连通图。连通分量,指的是无向图中的极大连通子图。图G1就是一个连通图,而G3则是非连通图,但G3有3个连通分量。


        (11)强连通图和强连通分量:在有向图G中,如果对于每一对vi,vj属于V,vi≠vj,从vi到vj和从vi到vj都存在路径,则称G是强连通图。有向图中的极大强连通子图称作有向图的强连通分量。
        G1不是强连通图,但它有两个强连通分量,下图为G1的两个强连通分量。


        (12)连通图的生成树:一个极小连通子图,它含有图中全部顶点,但只有足以构成一棵树的n-1条边,这样的连通子图称为连通图的生成树。如果在一棵生成树上添加一条边, 必定构成一个环,因为这条边使得它依附的那两个顶点之间有了第二条路径。
        一棵有n个顶点的生成树有且仅有n-1条边。如果一个图有n个顶点和小于n-1条边,则是非连通图。如果它多于n-1条边,则一定有环。但是,有n-1条边的图不一定是生成树。
        (13)有向树和生成森林:有一个顶点的入度为0,其余顶点的入度均为1的有向图称为有向树。一个有向图的生成森林是由若干棵有向树组成,含有图中全部顶点,但只有足以构成若干棵不相交的有向树的弧。

图的存储结构

邻接矩阵表示法

        邻接矩阵是表示顶点之间相邻关系的矩阵。设G(V,E)是具有n个顶点的图,则G的邻接矩阵是具有如下性质的n阶方阵:

下图为G1和G2的邻接矩阵:

 若G是网,则邻接矩阵可以定义为:

 w0表示边上的权值; ∞表示计算机允许的,大于所有边上权值的数。
下图为一个有向网和它的邻接矩阵。

        用邻接矩阵表示法表示图,除了一个用于存储邻接矩阵的二维数组外,还需要用一个一维数组来存储顶点信息。

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

#include <stdio.h>
#define OK 1
#define ERROR 0
#define INFEASIBLE -1
#define OVERFLOW -2
typedef int Status;
#define MaxInt 32767   //表示极大值,即∞
#define MVNum 100      //最大顶点数
typedef int VerTexType;//假设顶点的数据类型为整型
typedef int ArcType;   //假设边的权值类型为整型
typedef struct
{
	VerTexType vexs[MVNum];     //顶点表
	ArcType arcs[MVNum][MVNum]; //邻接矩阵
	int vexnum,arcnum;          //图的当前点数和边数
}AMGraph;

Status LocateVex(AMGraph *G,VerTexType v) //查询顶点v在图G中的下标位置
{
	for(int i=0;i<G->vexnum;i++){
		if(G->vexs[i]==v){
			return i;
		}
	}
}

//此处定义无向图的创建
Status CreateUDG(AMGraph *G){
	scanf("%d",&G->vexnum);
	scanf("%d",&G->arcnum);
	for(int i=0;i<G->vexnum;i++){
		scanf("%d",&G->vexs[i]);//输入点的信息 
	} 
	int v1,v2;
	int m,n;
	for(int j=0;j<G->arcnum;j++){
		scanf("%d",&v1);
		scanf("%d",&v2);
		int m=LocateVex(G,v1);
		int n=LocateVex(G,v2);
		G->arcs[m][n]=1;
		G->arcs[n][m]=1;
	}
	return OK;
}

//此处定义无向图的邻接矩阵的输出
Status PrintAMGraph(AMGraph *G){
	for(int i=0;i<G->vexnum;i++){
		for(int j=0;j<G->vexnum;j++){
			printf("%d ",G->arcs[i][j]);
		}
		printf("\n");
	}
	return OK;
}

int main()
{
	AMGraph G;
//调用利用邻接矩阵创建无向图的函数CreateUDG
	CreateUDG(&G);
//调用输出邻接矩阵的函数PrintAMGraph
	PrintAMGraph(&G);
	return 0;
}

邻接矩阵表示法的优缺点

(1)优点
①便于判断两个顶点之间是否有边,即根据4[i][j]=0或1来判断。
②便于计算各个顶点的度。对于无向图,邻接矩阵第i行元素之和就是顶点vi的度;对于有向图,第i行元素之和就是顶点vi的出度,第i列元素之和就是顶点i的入度。
(2)缺点
①不便于增加和删除顶点。
②不便于统计边的数目,需要查找邻接矩阵所有元素才能统计完毕,时间复杂度为O(n^2)。
③空间复杂度高。如果是有向图,n个顶点需要n^2个单元存储边。如果是无向图,因其邻接矩阵是对称的,所以对规模较大的邻接矩阵可以采用压缩存储的方法,仅存储下三角(或上三角)的元素,这样需要n(n- 1)/2个单元即可。但无论以何种方式存储,邻接矩阵表示法的空间复杂度均为O(n^2),这对于稀疏图而言尤其浪费空间。

邻接表表示法

        邻接表(Adjacency List)是图的一种链式存储结构。在邻接表中,对图中每个顶点vi建立一个单链表,把与vi相邻接的顶点放在这个链表中。邻接表中每个单链表的第一个节点存放有关顶点的信息,把这一节点看成链表的表头,其余节点存放有关边的信息,这样邻接表便由两部分组成:表头节点表和边表。
        (1)表头节点表:由所有表头节点以顺序结构的形式存储,以便可以随机访问任一顶点的边链表。表头节点包括数据域(data)和链域(firstarc)两部分。数据域用于存储顶点vi的名称或其他有关信息;链域用于指向链表中第一个节点(与顶点vi邻接的第一个邻接点)。
        (2)边表:由表示图中顶点间关系的2n个边链表组成。边链表中边节点包括邻接点域(adjvex)、数据域(info)和链域(nextarc) 3个部分。邻接点域指示与顶点vi邻接的点在图中的位置;数据域存储和边相关的信息,如权值等;链域指示与顶点vi邻接的下一条边的节点。

邻接表表示法创建无向图

#include<stdio.h>
#include<stack> 
#include <iostream>
#define MAXSIZE 100
#define MaxInt 32767 //表示最大值,即正无穷大 
#define MVNum 100 //定义最大顶点数 
using namespace std;
typedef char VerTexType; //假设顶点数据类型为字符型
typedef int ArcType; //假设边的权值为整型
//定义边结点 
typedef struct ArcNode{
	int adjvex;//该边所指向的顶点的位置
	struct ArcNode *nextarc; //下一条边的指针 
}ArcNode; 
//定义顶点结点信息
typedef struct VNode{
	VerTexType data;
	ArcNode *firstarc; //指向第一条依附该顶点的边的指针 
}VNode, AdjList[MVNum]; 
//定义邻接表的结构
typedef struct {
	AdjList vertices;
	int vexnum, arcnum; //当前图的点数和边数 
}ALGraph;
//确定顶点vex在G.vertices中的序号
int LocateVex(ALGraph &G, VerTexType vex){
	for(int i=0;i<G.vexnum;i++){
		if(G.vertices[i].data==vex){
			return i;
		}
	}
} 
 
//邻接矩阵法构造无向图 
void CreateUGD(ALGraph &G){
	cin>>G.vexnum;
	cin>>G.arcnum;
	//输入各点,构造表头结点表
	for(int i=0;i<G.vexnum;i++){
		cin>>G.vertices[i].data;
		G.vertices[i].firstarc=NULL;
	} 
	//输入各边,构造邻接表 
	for(int k=0;k<G.arcnum;k++){
		VerTexType v1;
		cin>>v1;
		VerTexType v2;
		cin>>v2;
		//确定v1,v2在图G中的位置,即在G.vertices中的序号
		int i= LocateVex(G, v1);
		int j= LocateVex(G, v2);
		struct ArcNode *p1, *p2;
		p1=new ArcNode; //生成一个新的边结点 
		p1->adjvex=j;
		p1->nextarc=G.vertices[i].firstarc;
		G.vertices[i].firstarc=p1;
		p2=new ArcNode; //生成另一个对称边结点p2 
		p2->adjvex=i;
		p2->nextarc=G.vertices[j].firstarc;
		G.vertices[j].firstarc=p2;
	}
} 
 
//遍历图的邻接表 
void PrintfG(ALGraph &G){
	printf("遍历图的邻接表:\n");
	for(int i=0;i<G.vexnum;i++){
		printf("%c",G.vertices[i].data);
		ArcNode *p;
		p=G.vertices[i].firstarc;
		while(p){
			printf("->");
			printf("%d", p->adjvex);
			p=p->nextarc;
		}
		printf("^");
		printf("\n");
	}
}
 
int main(){
	ALGraph G;
	CreateUGD(G);
	PrintfG(G);
}

邻接表表示法的优缺点

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

  • 0
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
逻辑结构:描述数据元素之间的逻辑关系,如线性结构(如数组、链表)、树形结构(如二叉树、堆、B树)、结构(有向、无向等)以及集合和队列等抽象数据类型。 存储结构(物理结构):描述数据在计算机中如何具体存储。例如,数组的连续存储,链表的动态分配节点,树和的邻接矩阵或邻接表表示等。 基本操作:针对每种数据结构,定义了一系列基本的操作,包括但不限于插入、删除、查找、更新、遍历等,并分析这些操作的时间复杂度和空间复杂度。 算法: 算法设计:研究如何将解决问题的步骤形式化为一系列指令,使得计算机可以执行以求解问题。 算法特性:包括输入、输出、有穷性、确定性和可行性。即一个有效的算法必须能在有限步骤内结束,并且对于给定的输入产生唯一的确定输出。 算法分类:排序算法(如冒泡排序、快速排序、归并排序),查找算法(如顺序查找、二分查找、哈希查找),论算法(如Dijkstra最短路径算法、Floyd-Warshall算法、Prim最小生成树算法),动态规划,贪心算法,回溯法,分支限界法等。 算法分析:通过数学方法分析算法的时间复杂度(运行时间随数据规模增长的速度)和空间复杂度(所需内存大小)来评估其效率。 学习算法与数据结构不仅有助于理解程序的内部工作原理,更能帮助开发人员编写出高效、稳定和易于维护的软件系统。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值