数据结构与算法基础(六)图
提示:
前言
一、图的定义和基本术语
1.图
一个偶对 定义
1.1 无向图 有向图
1.2 完全图
有向完全图:从n个结点里任取2个(Cn2)*两条边
1.3 稀疏图 稠密图 网 邻接 关联(依附)
1.4 顶点的度(入度,出度)
1.5 路径 路径长度 环(回路) 简单路径 简单环(回路)
1.6 连通图(无向)强连通图(有项)
1.7 权、 网
1.8 子图
1.9 连通分量(强连通分量)极大连通子图
1.10 极小连通子图 生成树 生成森林
对比
二、案例引入–六度空间理论
三、图的类型定义
四、图的存储结构
1.how
图的逻辑结构:多对多
图没有顺序存储结构(用物理位置表示 太难)
数组表示法(邻接矩阵):借助二维数组 可以表示 元素间的关系
链式存储结构?
2.邻接矩阵表示法
- 顶点表(记录各个顶点信息)
- 邻接矩阵(各个顶点间的关系)
2.1 无向图的邻接矩阵
有几行几列:取决于顶点的个数
对角线全为0, 自身和自身没有关系
对称矩阵,互为邻接关系
2.2 有向图的邻接矩阵
行,出度:选取一行,看这个结点发出的弧
2.3 网(有权图)的邻接矩阵表示法
3.邻接矩阵的建立
3.1 邻接矩阵的存储表示:两个数组
- 顶点表(一维数组)
- 邻接矩阵(二维数组)
3.2 采用邻接矩阵表示法创建无向网
无向网------无向图 有向网 有向网就都会了
算法思想:
1. 输入 总顶点数 总边数
2. 输入 点的信息存入 顶点表
3. 初始化 邻接矩阵,每个权值 初始化为极大值
4. 构造 邻接矩阵
AMGraph;
//用邻接矩阵表示 的 图 Adjacency Matrix Graph
Status CreateUDN(AMGraph &G){//这个G有四个成员
//顶点表vexs[] 边表 arcs[][]
// 顶点数目vexnum 边数目arcnum G. 成员引用符操作成员变量;采用邻接矩阵表示法,创建无向网G
cin>>G.vexnum>>G.arcnum;//输入总顶点数,总边数
//建立顶点表
for(i=0;i<G.vexnum;++i)
cin>>G.vexs[i];//依次输入点的信息
//建立邻接矩阵
for(i=0;i<G.vexnum;++i)
for(j=0;j<G.vexnum;++j)
G.arcs[i][j]=MaxInt; //初始化:边的权值均置为极大值
//构造邻接矩阵
for(k=0;k<G.arcnum;++k){
cin>>v1>>v2>>w; // 要输和入 一条边所依附的两个顶点、权值W
i=LocateVex(G,v1);//在顶点表中查找顶点的位置(下标)
j=LocateVex(G,v2);//
G.arcs[i][j]=w; //边<v1,v2>的 权值置为w
G.arcs[j][i]=G.arcs[i][j];// 边<v1,v2>的对称边<v2,v1>的权值也置为w
}// for
return OK;
}//CreateUDN
//补充算法:在图中查找顶点
int LocateVex(AMGraph G, VertexType u){
//在图G中查找顶点u,存在则返回顶点表中的下标;否则 返回-1
int i;
for(i=0;i<G.vexnum;++i)
if(u==G.vexs[i]) return i;
return -1;
}
3.3 邻接矩阵的优缺点
优点:
-直观、简单
-方便检查 任意一对顶点间是否在边
-方便找任一顶点的 所有 邻接点(有边直接相连的点)
-可计算任意结点的 度,对于有向图,一行加起来就是入度/一列相加就是出度
缺点:
-不便于增加、删除结点:得将矩阵增加、减少一行/列
-浪费空间:对稀疏图(点很多 边很少)有大量的无效元素
对稠密图(完全图)还是很合算的
-浪费时间:统计稀疏图中一共有多少条边,必须全部遍历O(n^2)
4.邻接表表示法(链式)
4.1 无向图的邻接表
4.2 有向图的邻接表
存储空间为n+e
=====>图的结构定义
5.采用邻接表 表示法创建无向网
// 头插法,将i位置的地址赋值给 j结点的指针域
b->e
e->b
6. 邻接表的优缺点
7. 邻接矩阵 和 邻接表 表示法的关系
//和算法有关(头插?尾插)
//尤其是稀疏图时
8.改进
8.1 十字链表—用于有向图
出度 结合 入度
8.2 邻接多重表
P121 (邻接多重表)
五、图的遍历
遍历的定义
1.深度优先遍历(DFS)
回溯
1.1 算法实现(邻接矩阵)
算法:采用 邻接矩阵 表示图的深度优先搜索遍历
void DFS(AMGraph G,int v){// 图G为邻接矩阵类型
//访问第v个顶点
cout<<v;
visited[v]=true;
for(w=0;w<G.vexnum;w++) //依次检查邻接矩阵v所在的行
if((G.arcs[v][w]!=0)&&(!visited[w])) //是邻接点 且 未被访问过
DFS(G,w); // 满足判断,递归调用DFS
}
1.2 非连通图的遍历
思路: 找到当前点的邻接点–看看访问过与否----访问过则退回上一级
2. 广度优先遍历(BFS)
2.1 算法实现
循环队列 顺着表,找同一层的邻接点分别入队
void BFS (Graph G, int v){ //按广度优先非递归遍历连通图G
cout<<v; visited[v]=true; //访问第v个顶点
InitQueue(Q); //初始化 辅助队列Q 置空
EnQueue(Q,v); //v进队
While(!QueueEmpty(Q)){ //队列非空
DeQueue(Q,u);// 队头元素 出队 并置为u
for(w=FirstAdjVex(G,u);w>=0;w=NextAdjVex(G,u,w))// w为u的尚未访问的邻接结点
if(!visited[w]){
cout<<w; visited[w]=true; EnQueue(Q,w);//w进队
}//if
}//while
}//BFS
六、图的应用
1.最小生成树
连通、无回路、所有结点、边为n-1
生成树:所有顶点均由边连在一起,但不存在回路
1.1 无向图的生成树
深度遍历&回溯
广度遍历
1.2 最小生成树
1.2.1 构造最小生成树MST
1.2.2 Prim算法
1.2.3 Kruskal克鲁斯卡尔 算法
贪心算法,直接把所有节点加进集合,选择小的边,一条一入一条加入
只有顶点,无边======不能形成环
2. 最短路径
2.1 Dijkstra(迪杰斯特拉)算法
最终求得的是v0到各个节点的最短路径
所以v2加入,看加入后v0到各点的距离,有无因为v2的加入而变短
Dijkstra(迪杰斯特拉)算法的时间复杂度O(n^2)
要求 所有顶点间欸但最短路径 :
法一:每次以一个顶点为源点,重复执行Dijkstra :n* O(n^2)-----> n ^3
法二:弗洛伊德(Floyd)算法
2.2 弗洛伊德(Floyd)算法
3. 拓扑排序
3.1 有向无环图
3.2 AOV网 AOE网
AOV网的特点:
3.3 拓扑排序定义
网-----> 线性序列
4. 关键路径
4.1 引入
{??????????????????}
结点 的ve(最早发生时间) 算最大值----正着
结点的最迟发生时间—最小值 —反着从右
弧(活动的发生时间) a
活动的最早发生时间==活动起始顶点的最早发生时间
活动最迟发生时间==活动 终止点的最晚发生时间-权值