第六章 图(更新中....)

图不同于树,树是一种具有层次关系的结构,而在图中的任意两个顶点都可能有关系。

图(Graph),顶点(Vertex),边(Edge)
因此图可以表示为: G=(V,E),每一条边是一顶点对(V,W)。

图和线性表,树的比较:
1.线性表中的数据叫元素,树中的数据叫结点,图中的数据叫顶点
2.线性表中没有元素叫空表,树中没有结点叫空树,图中必须至少有一个顶点,但是边集可以为空

图的相关术语:

1.无向图(Undirected Graphs)
2.有向图(Directed Graphs)
3.简单图(Simple Graph):无重边,无自回路边
4.邻接点(Adjacent Vertices)
5.路径,简单路径,回路,无环图
6.完全图:任意两个顶点都有边相连
7.顶点的度,出度,入度
8.稠密图(Dense Graph),稀疏图(Sparse Graph)
9.权,网图:通常情况下,网图简称图
10.子图(Subgraph)
11.
在无向图中:
连通图,连通分量(包括子图,连通,极大顶点数,极大边数)
12:
在有向图中:
强连通图,强连通分量
13.生成树(Spanning Tree)
14.生成森林(Spanning Forest)

图的操作集:

  1. Graph CreateGraph(int VertexNum);
  2. void InsertEdge(Graph G, Edge E);
  3. void DeleteEdge(Graph G, Edge E);
  4. void IsEmpty(Graph G);
  5. void DFS(Graph G, Vertex V, (* Visit)(Vertex));
  6. void BFS(Graph G, Vertex V, (*Visit)(Vertex));

图的存储结构:

邻接矩阵:(Adjacency Matrix)
在无向图中,用邻接矩阵可能会造成空间的浪费,所以我们用一个一维数组来存储,长度为N*(N+1)/2,那么Gij在数组中的对应的下标为:i*(i+1)/2+j

用邻接矩阵存储图的时候,除了用一组二维数组存储表示顶点间相邻关系的邻接矩阵外,还需要一个一维数组来存储顶点信息,另外还有图的顶点数和边数

#define MaxVertexNum 100
#define INFINITY 65535
typedef int Vertex;
typedef int WeightType;
typedef char DataType;

typedef struct GNode* PtrToGNode;
struct GNode
{
	int Nv;
	int Ne;
	WeightType G[MaxVertexNum][MaxVertexNum];
	DataType Data[MaxvertexNum];
};
typedef PtrToGNode MGraph;

无向网图的初始化程序:

//边的定义
typedef struct ENode* PtrToENode;
struct ENode
{
	Vertex V1,V2;
	WeightType Weight;
};
typedef PtrToENode Edge;

MGraph CreateGraph(int VertexNum)
{
	Vertex V, W;
	MGraph Graph;
	Graph =(MGraph)malloc(sizeof(struct GNode));
	Graph->Nv = VertexNum;
	Graph->Ne = 0;
	//初始化邻接矩阵
	for(V=0;V<Graph->Nv;W++)
		for(W=0;W<Graph->Nv;W++)
			Graph->G[V][W]=INFINITY;
	return Graph;
}

void InsertEdge(MGraph Graph, Edge E)
{
	Graph->G[E->V1]{E->V2] = E->Weight;
	Graph->G[E->V2][E->V1] = E->Weight;
}

MGraph BuildGraph()
{
	MGraph Graph;
	Edge E;
	Vertex V;
	int Nv,i;
	scanf("%d",&Nv);
	Graph = CreateGraph(Nv);
	scanf("%d",&(Graph->Ne));
	if(Graph->Ne!=0)
	{
		E = (Edge)malloc(sizeof(struct ENode));
		for(i = 0; i<Graph->Ne;i++)
		{
		scanf("%d %d %d",&E->V1,&E->V2,&E->Weight);
		InsertEdge(Graph, E);
		}
	}
	for(V=0;V<Graph->Nv;V++)
	scanf("%c",&(Graph->Data[V]));
	return Graph;
}

对于稠密图 ,这是一个很好的存储方法,但是对于稀疏图就会造成空间的浪费。

邻接表:(Adjacency Lists)

邻接表是图的一种顺序存储和链式存储相结合的存储方式
邻接表中有两种结点结构:一是顶点表的结点结构,由数据域和指向第一条邻接边的指针域构成。二是边表结点,由临界点域和指向下一条邻接边的指针域构成
邻接表结构的表示和声明:

#define MaxVertexNum 100
typedef int Vertex;
typedef int WeightType;
typedef char DataType;
//边的定义
typedef struct ENode* PtrToENode;
struct ENode
{
	Vertex V1,V2;
	WeightType Weight;
}
typedef PtrToENode Edge;
//邻接点的定义
typedef struct  AdjNode* PtrToAdjVNode;
struct AdjVNode
{
	Vertex Adjv;
	WeightType Weight;
	PrtToAdjVNode Next;
};
//顶点表头结点的定义
typedef struct Vnode
{
	PtrToAdjVNode FirstEdge;
	DataType Data;
}AdjList[MaxVertexNum];
//图结点的定义
typedef struct GNode* PtrToGNode;
struct GNode
{
	int Nv;
	int Ne;
	AdjList G;
};
typedef  PtrToGNode LGraph;

邻接表无向图的初始化:

LGraph CreateGraph(int VertexNum)
{
	Vertex V;
	LGraph Graph;
	Graph=(LGraph)malloc(sizeof(struct GNode));
	Graph->Nv = VertexNum;
	Graph->Ne = 0;
	for(v = 0;V<Graph->Nv;V++)
		Graph->G[V].FirstGraph = NULL;
	return Graph;
}
void InsertEdge(LGraph Graph, Edge E)
{
	PtrToAdjVNode NewNode;
	NewNode = (PtrToAdjVNode)malloc(sizeof(struct AdjVNode));
	NewNode->Adjv = E->V2;
	NewNode->Weight = E->Weight;
	NewNode->next = Graph->G[E->V1].FirstEdge;
	Graph->G[E->V1].FirstEdge = NewNode;
	//如果是无向图还需要插入边<V2,V1>
	NewNode = (PtrTiAdjVNode)malloc(sizeof(struct AdjVNode));
	NewNode->Adjv = E->V1;
	NewNode->Weight = E->Weight;
	NewNode->next = Graph->G[E->V2].FirstEdge;
	Graph->G[E->V2].FirstEdge = NewNode;
}
LGraph BuildGraph()
{
	LGraph Graph;
	Edge E;
	Vertex V;
	int Nv,i;
	scanf("%d",&Nv);
	Graph = CreateGraph(Nv);
	scanf("%d",&(Graph->Ne));
	if(Graph->Ne!=0)
	{
		E =(Edge)malloc(sizeof(struct ENode));
		for(i=0; i<Graph->Ne;i++)
		{
		scanf("%d %d %d", &E->V1, &E->V2,&E->Weight);
		InsertEdge(Graph,E);
		}
	}
	for(V=0;V<Graph->Nv;V++)
		scanf("%c",&(Graph->G[V].Data));
	return Graph;
}

图的遍历:

深度优先搜索:(Depth First Search,DFS)
类似于树的先序遍历,是先序遍历的推广,假设所有的结点都未被访问,从某个顶点开始一直访问接下来未被访问的邻接点,如果到底了,则进行递归退回操作,检查之前是否有没有被访问的邻接点。
邻接表存储图的深度优先遍历:

扫描二维码关注公众号,回复: 11587797 查看本文章
void Visit(Vertex V)
{
	printf("正在访问顶点%d\n",V);
}
//设置一个全局变量Visited[],来检查结点是否被访问,初始化为false
void DFS(LGraph Graph, Vertex V, void(*Visit)(Vertex))
{
	PtrToAdjVNode W;
	Visit(V);
	Visited[V] = true;
	for(W = Graph->G[V].FirstEdge;W;W=W->next)
		if(!Visited[W->AdjV])
			DFS(Graph, W->AdjV,Visit);
}

遍历图的过程实质上是对每个顶点查找其邻接点的过程。其耗费的时间取决于所采用的存储结构。当用邻接矩阵作为图的存储结构时,查找所有顶点的邻接点所需时间为(顶点数的平方),而以邻接表作为存储结构的时候,找邻接点的所需时间为(边的数量),所以当以邻接表作为存储结构,深度优先遍历的时间复杂度为(边的数量加上顶点的数量)

广度优先搜索:(Breadth First Search, BFS)
类似于树的层序遍历过程
通俗的说,广度优先搜索就像一个圆规,来画圆一样,一圈一圈的搜索,一圈一圈的扩大。

邻接矩阵存储图的广度优先遍历:

bool IsEdge(MGraph Graph, Vertex V, Vertex W)
{
	return Graph->G[V][W]<INFINITY?true:false;
}

void BFS(MGraph Graph, Vertex S, void(*Visit)(Vertex))
{
	Queue Q;
	Vertex  V,W;
	Q= CreateQueue(MaxSize);
	Visit(S);
	Visited[S] = true;
	AddQ(Q,S);
	while(!IsEmpty(Q))
	{
		V=DeleteQ(Q);
		for(W =0; W<Graph->Nv;W++)
			if(!Visited[W]&&IsEdge(Graph,V,W))
			{
				Visit(W);
				Visited[W] = true;
				AddQ(Q,W);
			}
	}
}

猜你喜欢

转载自blog.csdn.net/m0_43429389/article/details/107922742
今日推荐