《数据结构》第6章 图
第6章 图
6.1 图的定义和基本数据
概念总览:
- 图
- 无向图、有向图
- 完全图、稠密图、稀疏图
- 度、入度、出度、邻接点(相邻接)
- 连通图(=强连通图)、路径
- 权、网
- 子图
- 简单图
6.2 图的存储结构(邻接矩阵+邻接表)
6.2.1 邻接矩阵(顺序存储结构 两步:初始化(图0网∞)+1的修正)
1.无向图
2.有向图(行出尾,列入头)
3.无向网
4.有向网
6.2.2 邻接表(链式存储结构 表结点+头结点)
1.无向图
2.有向图(邻接表+逆邻接表)
3.网(数据结构的魅力)
6.3 图的遍历(2 深度by栈+广度by队列→生成树 都不唯一)
6.3.1 深度优先搜索DFS(Depth First Search)一路走到头,不撞墙不回头。回退去哪里找?去栈里,后进来的先出去。
6.3.2 广度优先搜索BFS(Breadth First Search)闪电 自上向下,自左向右。分组判断
6.4 图的应用(要掌握7个算法:最小生成树2+最短路径2+拓扑排序+关键路径)
一、生成树和最小生成树(4种方法,讲2个)
1.普里姆 prim 算法(稠密图 n个结点n步——初始化+n-1条边)
2.克鲁斯卡尔 kruskal 算法(稀疏图 更简单 n个结点n步——初始化+n-1条边)
二、最短路径
1.【常考】迪杰斯特拉 Dijkstra 算法(单源点 n个结点n步——初始化+n-1次变实线)
2.弗洛伊德 Floyd 算法(顶点对)
下图解释:
D数组:D0是左图的邻接矩阵表示,D1根据等式换算得到(第一次修正)。
出现问题:数据多的话容易混乱。→借助P数组(存放路径中其前驱顶点)
V2的前驱顶点是1,V1的前驱顶点是0→V0到V2的路径是0→1→2
阿杰算法只需要一维数组,弗洛伊德算法需要D、P两个二维数组
代码:3个嵌套,时间复杂度是O(n^3)
1.初始化
2.结果
P的理解:
对角线——自己到自己就填自己
V0到V1~V8都需要经过1
V3到V0~V2都需要经过4
三、关键路径(研究2个问题)
甘特图以图示通过活动列表和时间刻度表示出特定项目的顺序与持续时间。一条线条图,横轴表示时间,纵轴表示项目,线条表示期间计划和实际完成情况。直观表明计划何时进行,进展与要求的对比。便于管理者弄清项目的剩余任务,评估工作进度。
甘特图是以作业排序为目的,将活动与时间联系起来的最早尝试的工具之一,帮助企业描述工作中心、超时工作等资源的使用。 甘特图包含以下三个含义:
1、以图形或表格的形式显示活动; 2、通用的显示进度的方法; 3、构造时含日历天和持续时间,不将周末节假算在进度内。
简单、醒目、便于编制,在管理中广泛应用。 甘特图按内容不同,分为计划图表、负荷图表、机器闲置图表、人员闲置图表和进度表五种形式。
如何合理地干完工程?
- 能不能干完?(有没有环)——拓扑排序输出顶点序列
- 求关键路径
1.拓扑排序(AOV网,可判断一个图中是否存在环)
2.关键路径(AOE网 事件与活动)
事件瞬间 发生
活动持续一段 开始 结束
变量法
习题
1.例题
2.考研真题
#include <iostream>
#include "Stack.h"
#include <malloc.h>
using namespace std;
#define MAXVEX 14
#define MAXEDGE 20
typedef struct EdgeNode
{
int adjvex; // 邻接点域,存储该顶点对应的下标
struct EdgeNode* next; // 链域
} EdgeNode;
typedef struct VertexNode
{
int inNum; // 顶点入度值
int data; // 顶点数值欲
EdgeNode* firstedge; // 边表头指针
} VertexNode, AdjList[MAXVEX];
typedef struct
{
AdjList adjList;
int numVertexes, numEdges; // 图中当前顶点数和边数(对于本案例,已经存在宏定义)
} graphAdjList, *GraphAdjList;
// 构建节点
EdgeNode* BuyNode()
{
EdgeNode* p = (EdgeNode*)malloc(sizeof(EdgeNode));
p->adjvex = -1;
p->next = NULL;
return p;
}
// 初始化图
void InitGraph(graphAdjList& g)
{
for (int i = 0; i < MAXVEX; ++i)
{
g.adjList[i].firstedge = NULL;
}
}
// 创建图
void CreateGraph(graphAdjList& g)
{
int i = 0, begin = 0, end = 0;
EdgeNode *pNode = NULL;
cout << "输入14个顶点信息(顶点 入度):" << endl;
for (i = 0; i < MAXVEX; ++i)
{
cin >> g.adjList[i].data >> g.adjList[i].inNum;
}
cout << "输入20条边的信息:" << endl;
for (i = 0; i < MAXEDGE; ++i)
{
cin >> begin >> end;
pNode = BuyNode();
pNode->adjvex = end;
pNode->next = g.adjList[begin].firstedge;
g.adjList[begin].firstedge = pNode;
}
}
// 打印输入信息的逻辑图
void PrintGraph(graphAdjList &g)
{
cout << "打印邻接表的逻辑图:" << endl;
for (int i = 0; i < MAXVEX; ++i)
{
cout << " " << g.adjList[i].inNum << " " << g.adjList[i].data << " ";
EdgeNode* p = g.adjList[i].firstedge;
cout << ": ";
while (p != NULL)
{
int index = p->adjvex;
cout << g.adjList[index].data << " ";
p = p->next;
}
cout << endl;
}
}
bool TopologicalSort(graphAdjList g)
{
EdgeNode* pNode = NULL;
int i = 0, k = 0, gettop = 0;
int nCnt = 0;
SeqStack<int> sQ;
for (i = 0; i < MAXVEX; ++i)
{
if (0 == g.adjList[i].inNum)
sQ.Push(i);
}
while (!sQ.IsEmpty())
{
sQ.Pop(gettop);
++nCnt;
if (MAXVEX == nCnt)
{ //去掉拓扑路径后面的-->
cout << g.adjList[gettop].data;
break;
}
cout << g.adjList[gettop].data << "-->";
pNode = g.adjList[gettop].firstedge;
while (pNode != NULL)
{
k = pNode->adjvex;
--g.adjList[k].inNum;
if (0 == g.adjList[k].inNum)
sQ.Push(k);
pNode = pNode->next;
}
}
return nCnt != MAXVEX;
}
void main()
{
graphAdjList myg;
InitGraph(myg);
cout << "创建图:" << endl;
CreateGraph(myg);
cout << "打印图的邻接表逻辑结构:" << endl;
PrintGraph(myg);
cout << "拓扑排序路径:" << endl;
bool bAcire = TopologicalSort(myg);
cout << endl;
cout << "存在回环? " << bAcire << endl;
}
/*
创建图:
输入14个顶点信息(顶点 入度):
0 0
1 0
2 2
3 0
4 2
5 3
6 1
7 2
8 2
9 2
10 1
11 2
12 1
13 2
输入20条边的信息:
0 5
0 4
0 11
1 4
1 8
1 2
2 5
2 6
2 9
3 2
3 13
4 7
5 8
5 12
6 5
8 7
9 10
9 11
10 13
12 9
打印图的邻接表逻辑结构:
打印邻接表的逻辑图:
0 0 : 11 4 5
0 1 : 2 8 4
2 2 : 9 6 5
0 3 : 13 2
2 4 : 7
3 5 : 12 8
1 6 : 5
2 7 :
2 8 : 7
2 9 : 11 10
1 10 : 13
2 11 :
1 12 : 9
2 13 :
拓扑排序路径:
3-->1-->2-->6-->0-->5-->8-->12-->9-->10-->13-->11-->4-->7
存在回环? 0
*/