浙大数据结构 第六讲 图(上)

6.1 什么是图

图表示“多对多”的关系

图包含:

  1. 一组顶点:通常用V(Vertex)表示顶点集合
  2. 一组边:通常用E(Edge)表示边的集合
    边是顶点对:(v,w)∈E,其中v,w ∈ V
    有向边<v,w>表示从v指向w的边(单行线)
    不考虑重边和自回路
    浙大数据结构 第六讲 图(上)
    浙大数据结构 第六讲 图(上)

抽象数据类型定义

类型名称:图(Graph)
数据对象集:G(V,E)由一个非空的有限顶点集合V和一个有限边集合E组成。
操作集:对于任意图G ∈ Graph,以及v ∈ V,e ∈ E

Graph Create();//建立并返回空图
Graph InsertVertex(Graph G,Vertex V);//将一个顶点v插入G
Graph InsertEdge(Graph G,Edge e);//将一条边e插入G
void DFS(Graph G,Vertex V);//从顶点V出发优先遍历图G
void BFS(Graph G,Vertex V);//从顶点V出发宽度优先遍历图G
void ShortestPath(Graph G,Vertex V,int Dist[]);//计算图G中顶点v到任意其他顶点的最短距离
void MST(Graph G);//计算图G的最小生成树 

怎么在程序中表示一个图

邻接矩阵

浙大数据结构 第六讲 图(上)
浙大数据结构 第六讲 图(上)
邻接矩阵——有什么好处?

  1. 直观、简单、好理解
  2. 方便检查任意一堆顶点间是否存在边
  3. 方便找任一顶点的所有“邻接点”(有边直接相连的顶点)
  4. 方便计算任一顶点的“度”(从该点发出的边数为“出度”,指向该点的边数为“入度”)
    无向图:对应行(或列)非0元素的个数
    有向图:对应行非0元素的个数是“出度”,对应列非0元素的个数是“入度”

邻接矩阵——有什么坏处?

  1. 浪费空间——存稀疏图(点很多而边很少),有大量无效元素,但对稠密图(特别是完全图)还是很合算的
  2. 浪费时间——统计稀疏图中一共有多少条边

邻接表

邻接表:G[N]为指针数组,对应矩阵每行一个链表,只存非0元素
浙大数据结构 第六讲 图(上)
好处:

  1. 方便找任一结点的所有“邻接点”
  2. 节约稀疏图的空间:需要N个头指针 + 2E个结点(每个结点至少2个域)
  3. 方便计算无向图中任一结点的“度”

不足:

  1. 对无向图,只能计算“出度”;需要构造“逆邻接表”(存指向自己的边)来方便计算“入度”
  2. 不方便检查任意一对顶点间是否存在边

课后练习

浙大数据结构 第六讲 图(上)
浙大数据结构 第六讲 图(上)

浙大数据结构 第六讲 图(上)

6.2 图的遍历

深度优先搜索(Depth First Search,DFS)

例题引入:点亮视线范围内的一盏灯,当视线内的灯全部点亮时原路返回
浙大数据结构 第六讲 图(上)

void DFS(Vertex V)
{
	visited[V] = true;
	for(V的每个邻接点W)
		if(!visited[W]) DFS(W);
}

若有N个顶点、E条边,时间复杂度是
用邻接表存储图,有O(N+E)
用邻接矩阵存储图,有O(N^2)

广度优先搜索(Breadth First Search,BFS)

与树的层序遍历思路类似

void BFS(Vertex V)
{
	visited[V] = true;
	Enqueue(V,Q);
	while(!Isempty(Q))
	{
		V = Dequeue(Q);
		for( V 的每个邻接点 W )
			if(!visited[W])
			{
				visited[W] = true;
				Enqueue(W,Q);
			}
	}
}

若有N个顶点、E条边,时间复杂度是
用邻接表存储图,有O(N+E)
用邻接矩阵存储图,有O(N^2)

两种遍历方法的比较

浙大数据结构 第六讲 图(上)
浙大数据结构 第六讲 图(上)
讨论6.2 把迷宫出口换到哪里就该BFS不爽了?
右下角。BFS需要访问几乎所有节点才可以访问到出口。

总结

BFS是围绕某个点一层一层的进行遍历,借助队列的数据结构实现。
DFS是从一个点出发一直往深处遍历,条件不符就折返,通过栈的数据结构实现。
在正常情况下二者的时间复杂度都是相同的O(v+e),但是在内存的使用上BFS由于要使用队列存储所有的外层节点,所以内存消耗相对较大。

从宏观上来看:
如果你已经知道解离根节点比较近,那么BFS更好
如果整体上每个节点的边很多,那么BFS消耗的内存会很大
如果一棵树很深而解很少,那么DFS可能会很慢(相反如果解很多并且都比较深的话,那么BFS就会很慢)从名字上就很容易得出,
如果一个问题深度无穷而广度有限,那么DFS就没法获得解,但BFS可以,反之也同理。
BFS具有时间优势,因为它不用走回头路,DFS具有空间优势,因为同等情况下,DFS只保存了这一条路线的数据

图连不通怎么办?

连通: 如果从v到w存在一条(无向)路径,则称v和w是连通的。
路径: v到w的路径是一系列顶点{V,v1,v2,...,vn,W}的集合,其中任一对相邻的顶点间都有图中的边。路径的长度是路径中的边数(如果带权,则是所有边的权重和)。如果v到w之间的所有顶点都不同,则称简单路径
回路:起点等于终点的路径
连通图:图中任意两顶点均连通
连通分量:无向图的极大连通子图
极大顶点数:再加1个顶点就不连通了
极大边数:包含子图中所有顶点相连的所有边
浙大数据结构 第六讲 图(上)
对于无向图
强连通: 有向图中顶点V和W之间存在双向路径,则称V和W是强连通的
强连通图:有向图中任意两顶点均强连通
强连通分量:有向图的极大强连通子图
浙大数据结构 第六讲 图(上)
后两个图为G的强连通分量
浙大数据结构 第六讲 图(上)
ListComponents能够遍历 不连通的图 上所有结点

练习题
浙大数据结构 第六讲 图(上)
浙大数据结构 第六讲 图(上)

小白专场:建立一个图

建立邻接矩阵图

#include<iostream>
#include<cstdio>
#include<cstdlib>
using namespace std;
typedef struct GNode* PtrToGNode;
typedef struct ENode* PtrToENode;
typedef PtrToGNode MGraph;//以邻接矩阵存储的图类型
typedef PtrToENode Edge;
typedef int WeightType;
typedef int Vertex;
const int MaxVertexNum = 100;
struct GNode
{
	int Nv;//顶点数
	int Ne;//边数
	WeightType G[MaxVertexNum][MaxVertexNum];
};
struct ENode
{
	Vertex V1, V2;//有向边<V1,V2>
	WeightType Weight;//权重
};

MGraph CreateGraph(int VertexNum)
{
	Vertex V, W;
	MGraph Graph;
	Graph = (MGraph)malloc(sizeof(struct GNode));
	Graph->Nv = VertexNum;
	Graph->Ne = 0;
	for (int V = 0;V < Graph->Nv;V++)
	{
		for (int W = 0;W < Graph->Nv;W++)
		{
			Graph->G[V][W] = 0;
		}
	}
	return Graph;
}
void InsertEdge(MGraph Graph, Edge E)
{
	Graph->G[E->V1][E->V2] = E->Weight;
	Graph->G[E->V2][E->V1] = E->Weight;//若是无向图,还要插入边<V2,V1>
}
MGraph BuildMGraph()
{
	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 (int V = 0;V < Graph->Nv;V++)
		scanf(" %c", &Graph->Data[V]);*/
	return Graph;
}

简化版(考试专用)

#include<iostream>
#include<cstdio>
#include<cstdlib>
using namespace std;
const int MAXN = 1000;
int G[MAXN][MAXN], Nv, Ne;
void BulidGraph()
{
	scanf("%d", &Nv);
	for (int i = 0;i < Nv;i++)
		for (int j = 0;j < Nv;j++)
			G[i][j] = 0;
	scanf("%d", &Ne);
	int v1, v2, w;
	for (int i = 0;i < Ne;i++)
	{
		scanf("%d %d %d", &v1, &v2, &w);
		G[v1][v2] = 1;
		G[v2][v1] = 1;
	}
}
上一篇:【已解决】Win10 桌面空白处右键卡死


下一篇:cf340 D. Bubble Sort Graph(思维,最长上升子序列)