数据结构(浙江大学)—图

什么是图

1.定义

线性表:一对一
树:多对一
图:多对多(线性表和树是图的特殊情况)

包含:

  1. 一组顶点:通常用V (Vertex) 表示顶点集合
  2. 一组边:通常用E (Edge) 表示边的集合

2.抽象数据类型定义

类型名称:图(Graph)
数据对象集: G ( V , E ) G(V,E) G(V,E)由一个非空的有限顶点集合V和一个有限边集合E组成。
操作集:对于任意图 G ∈ G r a p h G \in Graph GGraph,以及 v ∈ V v \in V vV, e ∈ E e \in E eE
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的最小生成树;

3.在程序中表示

邻接矩阵 G [ N ] [ N ] G[N][N] G[N][N]——N个顶点从0到N-1编号

G [ i ] [ j ] = { 1 若<vi,vj>是G中的边 0 否则 G[i][j]= \begin{cases} 1& \text{若<vi,vj>是G中的边}\\ 0& \text{否则} \end{cases} G[i][j]={10<vi,vj>G中的边否则

在这里插入图片描述
对于无向图的存储,怎样可以省一半空间?

  • 用一个长度为N(N+1)/2的1维数组A存储{G00,G10,G11,……,G~n-1 0,…,Gn-1~ n-1},则Gij在A中对应的下标是: ( i ∗ ( i + 1 ) / 2 + j ) ( i*(i+1)/2 + j ) (i(i+1)/2+j)

邻接表

G[N]为指针数组,对应矩阵每行一个链表,只存非0元素
在这里插入图片描述

图的遍历

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

类似于树的先序遍历

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

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

void BFS ( Vertex V )
{ visited[V] = true;
	Enqueue(V, Q);   //将图V用队列存储  进
	while(!IsEmpty(Q)){
		V = Dequeue(Q);   //出
		for ( V 的每个邻接点W )
			if ( !visited[W] ) {
				visited[W] = true;
				Enqueue(W, Q);
		}
	}
}

3.连通

连通:如果从V到W存在一条**(无向)路径**,则称VW连通
路径:V到W的路径是一系列顶点{V, v1, v2, …,vn, W}的集合,(任一对相邻的顶点都有图中的边)。
路径的长度是路径中的边数(如果带权,则是所有边的权重和)。
如果V到W之间的所有顶点都不同,则称简单路径
回路:起点等于终点的路径,属于复杂路径
连通图:图中任意两顶点均连通
连通分量:无向图的极大连通子图

  1. 极大顶点数:再加1个顶点就不连通了
  2. 极大边数:包含子图中所有顶点相连的所有边

在这里插入图片描述
强连通: 有向图中顶点V和W之间存在双向路径,则称V和W是强连通的
强连通图: 有向图中任意两顶点均强连通
强连通分量:有向图的极大强连通子图
在这里插入图片描述

最短路径问题

1.定义

在网络中,两个不同顶点的所有路径中,边的权值之和最小的那一条路径,为最短路径
第一个顶点为源点(Source)
最后一个顶点为终点(Destination)

2.问题分类

单源:从某固定源点出发,求其到所有其他顶点的最短路径

  • (有向)无权图
  • (有向)有权图

多源:求任意两顶点间的最短路径

3.无权图的单源最短路算法

算法解析

//s到w的最短路径问题
void Unweighted ( Vertex S )
{ Enqueue(S, Q); //将源点压入
	while(!IsEmpty(Q)){
		V = Dequeue(Q); //当顶点被弹出时,表示v到源点的最短路径已被找到 
		for ( V 的每个邻接点W )
			if ( dist[W]==-1 ) {  //初始化距离为-1,表示w未被访问过
				dist[W] = dist[V]+1; //w最短距离=v的距离+1
				path[W] = V; //s 到 w 的必经顶点就是前一个顶点 v
				Enqueue(W, Q);
			  }
	}
}

4.有权图的单源最短路算法

Dijkstra 算法

算法解析

void Dijkstra( Vertex s )
{ while (1) {
	V = 未收录顶点中dist最小者;
	if ( 这样的V不存在)
		break;
	collected[V] = true;
	for ( V 的每个邻接点W )
		if ( collected[W] == false )
			if ( dist[V]+E<V,W> < dist[W] ) {
				dist[W] = dist[V] + E<V,W> ;
				path[W] = V;
				}
	}
} /* 不能解决有负边的情况*/

5.多源最短路算法

Floyd 算法

算法描述:
在这里插入图片描述

void Floyd()
{ for ( i = 0; i < N; i++ )
	for( j = 0; j < N; j++ ) {
		D[i][j] = G[i][j];
		path[i][j] = -1;
		}
	for( k = 0; k < N; k++ )
		for( i = 0; i < N; i++ )
			for( j = 0; j < N; j++ )
				if( D[i][k] + D[k][j] < D[i][j] ) {
					D[i][j] = D[i][k] + D[k][j];
					path[i][j] = k;
					}
}

最小生成树问题

1.定义

  1. 是一棵
    无回路
    |V|个顶点一定有|V|-1条边
  2. 生成
    包含全部顶点
    |V|-1条边都在图里
  3. 边的权重和最小
    tip:向生成树中任加一条边都一定构成回路
    最小生成树存在↔ 图连通
    在这里插入图片描述

2.贪心算法

约束:

  • 只能用图里有的边
  • 只能正好用掉|V|-1条边
  • 不能有回路

Prim算法 — 让一棵小树长大

在这里插入图片描述

//tip:初始化
//dist[V] = E(s,V)(已被收录)或正无穷(未被收录);  parent[s] = -1(根结点)
void Prim()
{ MST = {s};
	while (1) {
		V = 未收录顶点中dist最小者;//dist顶点V到生成树(所有收录进去的顶点)的最小距离
		if ( 这样的V不存在)
			break;
		将V收录进MST: dist[V] = 0;
		for ( V 的每个邻接点W )
			if ( W未被收录)//即dist[W]!=0
				if ( E(V,W) < dist[W] ){
					dist[W] = E(V,W) ;
					parent[W] = V;//每个顶点储存的是父结点的编号
				}
	}
	if ( MST中收的顶点不到|V|)
		Error ( “生成树不存在/图不连通” );
}

时间复杂度:T = O( ∣ V ∣ 2 |V|^2 V2) —— 稠密图合算

Kruskal算法— 将森林合并成树

将每个顶点都看成一棵树
在这里插入图片描述

void Kruskal ( Graph G )
{ MST = { } ;//收集的是边
	while ( MST 中不到|V|-1 条边&& E中还有边) {
		从E 中取一条权重最小的边E(v,w) ;//最小堆E(v,w)从E 中删除;
		if ( E(V,W)不在MST中构成回路)//并查集:每个节点看成树,若两个结点在一棵树里面,则E(V,W)加入必构成回路E(V,W) 加入MST;
		else
			彻底无视E(V,W);
	}
	if ( MST 中不到|V|-1 条边)
		Error ( “生成树不存在” );
}

T = O( |E| log |E| )

拓扑排序

0.背景例题:专业排课

在这里插入图片描述

1.定义

拓扑序:图中从V到W有一条有向路径,则V排在W之前.满足此条件的顶点序列称为一个拓扑序

2.AOV网络

AOV(activity on vertex):用图的顶点来代替一项工作的网络,又称顶点活动网络
如果有合理的拓扑序,则必定是有向无环图(否则v必须在v开始前结束矛盾❌)

void TopSort()
{ for ( cnt = 0; cnt < |V|; cnt++ ) {
		V = 未输出的入度为0的顶点;
		if ( 这样的V不存在) {
			Error ( “图中有回路” );
			break;
		}
		输出V,或者记录V的输出序号;
		for ( V 的每个邻接点W )
			Indegree[W]––;
		}
}

随时将入度变为0的顶点放到一个容器里

void TopSort()
{ for ( 图中每个顶点V )
	if ( Indegree[V]==0 )
		Enqueue( V, Q );
	while ( !IsEmpty(Q) ) {
		V = Dequeue( Q );
		输出V,或者记录V的输出序号; cnt++;
		for ( V 的每个邻接点W )
			if ( ––Indegree[W]==0 )
				Enqueue( W, Q );
	}
	if ( cnt != |V| )
		Error( “图中有回路” );
}

3.AOE网络

AOE(activity on edge):用边来代替一项工作的网络

在这里插入图片描述
关键路径问题:由绝对不允许延误的活动组成的路径
在这里插入图片描述

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值