这个作业属于哪个班级 | 数据结构--网络2011/2012 |
---|---|
这个作业的地址 | DS博客作业04--图 |
这个作业的目标 | 学习图结构设计及相关算法 |
姓名 | 王博 |
0.PTA得分截图
图
题目集总得分,请截图,截图中必须有自己名字。题目至少完成2/3,否则本次作业最高分5分。
1.本周学习总结(6分)
本次所有总结内容,请务必自己造一个图(不在教材或PPT出现的图),围绕这个图展开分析。建议:Python画图展示。图的结构尽量复杂,以便后续可以做最短路径、最小生成树的分析。
1.1 图的存储结构
1.1.1 邻接矩阵(不用PPT上的图)
- 造一个图,展示其对应邻接矩阵
-
邻接矩阵的结构体定义
#define MAXV<最大顶点数> typedef struct { int no; INfoType info; }VertcxRype; typedef struct { int edges[MAXV][MAXV]; int n, e;//顶点数,边数 VertcxRype vexs[MAXV]; }MatGraph;
-
建图函数
void CreateMGraph(MGraph &g, int n, int e)//建图 { //n顶点,e弧数 g.n = n; g.e = e; int i, j; int a, b; for (i = 1; i <= n; i++) { for (j = 1; j <= n; j++) { g.edges[i][j] = 0; } } for (i = 1; i <= e; i++) { cin >> a >> b; g.edges[a][b] = 1; g.edges[b][a] = 1; } }
1.1.2 邻接表
- 造一个图,展示其对应邻接表(不用PPT上的图)
-
邻接表的结构体定义
typedef struct ANode { int adjvex;//该边的终点编号 struct ANode *nextarc;//指向下一条边的指针 InfoType info; }ArcNode; typedef struct Vnode { Vertex data; ArcNode *firstarc;//指向第一条边的顶点 }VNode; typedef struct { VNode dajlist[MAXV];//邻接表 int n, e; }AdjGraph;
-
建图函数
void CreateAdj(AdjGraph *&G, int n, int e)//创建图邻接表 { int i, j, a, b; G = new AdjGraph; for (i = 1; i <= n; i++)//邻接表头结点置零 { G->adjlist[i].firstarc = NULL; } for (j = 1; j <= e; j++)//无向图 { cin >> a >> b; ArcNode *p,*q; p = new ArcNode; q = new ArcNode; p->adjvex = b;//用头插法进行插入 q->adjvex = a; p->nextarc = G->adjlist[a].firstarc; G->adjlist[a].firstarc = p; q->nextarc = G->adjlist[b].firstarc; G->adjlist[b].firstarc = q; } G->n = n; G->e = e; }
1.1.3 邻接矩阵和邻接表表示图的区别
各个结构适用什么图?时间复杂度的区别。
- 对于任一确定的无向图,邻接矩阵是唯一的(行列号与顶点编号一致),但邻接表不唯一(链接次序与顶点编号无关)。
- 邻接矩阵的时间复杂度为0(n2),而邻接表的时间复杂度为0(n+e)。
- 邻接矩阵多用于稠密图的存储(e接近n(n - 1)/2),而邻接表多用于稀疏图的存储(e<<n2)。邻接矩阵是不错的图存储结构,但是,对于边数相对于顶点数量比较少的图来说,会造成极大的资源浪费,而邻接表就解决了这个问题
1.2 图遍历
1.2.1 深度优先遍历
-
选上述的图,继续介绍深度优先遍历结果
好像介绍不了0-1-2-3
-
深度遍历代码
void DFSTraverse(MGraph* G) { int i = 0; /* 把每一个定点都设为未访问过 */ for(i = 0; i < G->numVertexes; i++) { visited[i] = 0; } /* 对未访问过的定点调用DFS */ for(i = 0; i < G->numVertexes; i++) { if(visited[i] == 0) { DFS(G,i); } } }
-
深度遍历适用哪些问题的求解。(可百度搜索)
图的深度遍历可以找到两点之间的全部路径,以此可以找到迷宫问题的全部可能答案,同时可以判断是否有简单路径,测试图的结构是否正确。
1.2.2 广度优先遍历
-
选上述的图,继续介绍广度优先遍历结果
0123
-
广度遍历代码
void BFS(AdjGraph *G, int v)//v节点开始广度遍历 { ArcNode *p;//新建结点储存当前信息 queue<int>q; cout << v; q.push(v); visited[v] = 1;//已访问 int w; while (!q.empty()) { w = q.front(); q.pop(); p = G->adjlist[w].firstarc; while (p != NULL)//遍历当前链 { if (visited[p->adjvex] == 0)//未访问过 { visited[p->adjvex] = 1; cout << " " << p->adjvex; q.push(p->adjvex); } p = p->nextarc; } } }
-
广度遍历适用哪些问题的求解。(可百度搜索)
图的广度遍历可以以层次型遍历图找到特定的第几个或者最近最远的顶点,可求解迷宫问题的最短路径。
1.3 最小生成树
用自己语言描述什么是最小生成树。
一个图的极小连通子图的权值之和最小构造出的树为最小生成树。
1.3.1 Prim算法求最小生成树
- 基于上述图结构求Prim算法生成的最小生成树的边序列
-
实现Prim算法的2个辅助数组是什么?其作用是什么?Prim算法代码。
#define INF 32767 void Peim(MGraph g, int v) { int lowcost[MAXV]; int min; int closest[MAXV]; int i, j, k; for (i = 0; i < g.n; i++) { lowcost[i] = g.edges[v][i];//置初值,放入顶点v和所有顶带你的权值 closest[i] = v; } for (i = 1; i < g.n; i++)//n-1条边,进行n-1次 { min = INF; for (j = 0; j < g.n; j++)//遍历找到权值最小的 { if (lowcost[j] != 0 && lowcost[j] < min) { min = lowcost[j]; k = j;//记录下标 } } lowcost[k] = 0;//lowcost为0表示该顶点已使用 for (j = 0; i < g.n; j++)//遍历所有顶点,比较找到的顶点与其他顶点的权值是否比原来小 { if (lowcsost[j] != 0 && g.edges[k][j] < lowcost[j]) { lowcost[j] = g.edges[k][j]; closest[j] = k;//改变权值和相邻的顶点 } } } }
-
分析Prim算法时间复杂度,适用什么图结构,为什么?
Prim()算法中有两重for循环,所以时间复杂度为O(n^2),其中n为图的顶点个数。Prim()算法的执行时间与图中的边数e无关,所以适合用稠密图求最小生成树。
1.3.2 Kruskal算法求解最小生成树
-
基于上述图结构求Kruskal算法生成的最小生成树的边序列
(1,3)(4,6)(2,5)(3,6)(2,3)
-
实现Kruskal算法的辅助数据结构是什么?其作用是什么?Kruskal算法代码。
void Kruskal(AdjGraph *g) { int i,j,u1,v1,sn1,sn2,k; int vest[MAXV];//集合辅组数组 Edge E[MaxSize];//存放所有边 k=0;//E数组的下标从0开始计 for(i=0;i<g.n;i++) { p=g->adjlist[i].firstarc; while(p!=NULL) { E[k].u=i;E[k].v=p->adjlist; E[k].w=p->weight; k++;p=p->nextarc; } InsertSort(E,g.e); for(i=0;i<g.n;i++) vest[i]=i; k=1; j=0; while(k<g.n) { u1=E[j].u;v1=E[j].v; sn1=vset[u1]; sn2=vset[v1]; if(sn1!=sn2) { printf("(%d,%d):%d ",u1,v1,E[j].w); k++; for(i=0;i<g.n;i++) if(vest[i]==sn2) vest[i]=sn1; } j++; } }
-
分析Kruskal算法时间复杂度,适用什么图结构,为什么?时间复杂的为O(elog2e),由于其与n无关,只与e有关,适用于稀疏图。
1.4 最短路径
1.4.1 Dijkstra算法求解最短路径
-
基于上述图结构,求解某个顶点到其他顶点最短路径。(结合dist数组、path数组求解)
-
Dijkstra算法需要哪些辅助数据结构
需要dist和path两个数组,其中dist数组用于存放最短的路径长度,通过比较其中元素找到最短路径,path数组则用于存放最短路径
-
Dijkstra算法如何解决贪心算法无法求最优解问题?展示算法中解决的代码。
void Dijkstra(MatGraph g, int v) { int dist[MAXV],path[MAXV]; int s[MAXV];//判断是否访问 int mindis, i, j, u; for (i = 0; i < g.n; i++) { dist[i] = g.edges[v][i];//初始化距离 s[i] = 0; if (g.edges[v]]i] < INF)//v到i有边,初始化前继结点 { path[i] = v; } else { path[i] = -1; } } s[v] = 1; for (i = 0; i < g.n; i++)//进行n-1次 { mindis = INF; for (j = 0; j < g.n; j++)//找到最小路径的长度 { if (s[j] == 0 && dist[j] < mindis) { u = j; mindis = dist[j]; } } s[u] = 1; for (j = 0; j < g.n; j++)//修改改变结点后的路径长度 { if (s[j] == 0) { if (g.edges[u][j] < INF&&dist[u] + g.edges[u][j] < dist[j])//修改此处可得到各种多种解法 { dist[j] = dist[u] + g.edges[u][j]; path[j] = u; } } } } }
-
Dijkstra算法的时间复杂度,使用什么图结构,为什么。
时间复杂度为O(n的平方),若是对n个顶点进行则是O(n的立方),更适用于邻接矩阵
1.4.2 Floyd算法求解最短路径
-
Floyd算法解决什么问题?
解各个顶点到某个顶点的最短路径的长度以及路径
-
Floyd算法需要哪些辅助数据结构
需要a和path两个二维数组,其中a数组是用于存放两个顶点之间的最短路径,path数组用于存放其的前继结点
-
Floyd算法优势,举例说明。
最短路径算法还有其他算法,可以自行百度搜索,并和教材算法比较。
求最短路径:
void Floyd(MatGraph g)
{
int A[MAXV][MAXV];
int path[MAXV][MAXV];
int i, j, k;
for (i = 0; i < g.n; i++)//进行初始化
{
for (j = 0; j < g.n; j++)
{
A[i][j] = g.edges[i][j];
if (i != j && g.edgse[i][j] < INF)//存在边的关系时
{
path[i][j] = i;
}
else
{
path[i][j] = -1;
}
}
}
for (k = 0; k < g.n; k++)
{
for (i = 0; i < g.n; i++)
{
for (j = 0; j < g.n; j++)
{
if (A[i][j] > A[i][k] + A[k][j])//找到更短路径
{
A[i][j] = A[i][k] + A[i][j];//修改路径长度
path[i][j] = k;//修改顶点
}
}
}
}
}
用于解决多源最短路径、无负权回路即可、边权可正可负、运行一次算法即可求得任意两点间最短路。
1.5 拓扑排序
-
找一个有向图,并求其对要的拓扑排序序列
-
实现拓扑排序代码,结构体如何设计?
结构体
typedef struct { Vertex data;//顶点信息 int count;//存放入度 AreNode *firstarc;//头结点类型 }VNode;
-
书写拓扑排序伪代码,介绍拓扑排序如何删除入度为0的结点?
拓扑排序每次找到入度为0为结点,每输出一个结点,其后续结点的入度减一,入度为0的结点进入栈或数组。
while()栈不为空 { 出栈,访问节点; 遍历邻接点 { 邻接点入度减一; 入度=0则入栈 } }
-
如何用拓扑排序代码检查一个有向图是否有环路?
若存在有环路导致某节点的入度恒不为0,导致其输出的顶点个数不全,则可通过判断输出的顶点个数可以判断是否有回路。
1.6 关键路径
- AOE网:带权的有向无环图,图中入度为0的顶点表示工程的开始事件,出度为0的顶点表示工程的结束事件,称这样的有向图为边表示活动的网(AOE网)。
- 通常每个工程都只有一个开始事件和结束事件,工程的AOE网都只有入度为0的顶点,称为源点,和一个出度为0的顶点,称为汇点。
- 关键路径:在AOE网中从源点到汇点的所有路径中最大路径长度的路径。
- AOE网中一条关键路径各活动持续时间的总和,把关键路径上的活动称为关键活动。
2.PTA实验作业(4分)
2.1 六度空间(2分)
选一题,介绍伪代码,不要贴代码。请结合图形展开分析思路。
2.1.1 伪代码(贴代码,本题0分)
伪代码为思路总结,不是简单翻译代码。
创建队列q,t记录节点所在层数,number记录与i距离不超过6的节点个数
i先进栈
置visited[i]=1
while队列不为空
令temp=出队节点
遍历temp所连接的节点
当该节点没有遍历过
把该节点入队列
number++
如果遍历到的节点在第六层,返回所得的number值
文
2.1.2 提交列表
2.1.3 本题知识点
邻接矩阵进行广度遍历,遍历进行层数的判断
2.2 旅游规划(2分)
2.2.1 伪代码(贴代码,本题0分)
伪代码为思路总结,不是简单翻译代码。
计算两个节点之间的最短路和计算最短路上的权值
首先定义的是邻接矩阵的数组和顶点数弧数
int know [MAXVEX]
know=1表示求得最短路径
int distance[MAXVEX]表示求的最短距离
int pay [MAXVEX]
表示最少费用
首先是主函数中输入题干中的信息然后执行创建图和找路径函数
if判断是否最短路径符合要求符合要求在进行输出
CreatGraph函数使用邻接矩阵建图函数中是相应的操作
接下来是重要的 Dijkstra找路径函数
定义ij作为循环变量k作为记录节点位置的变量
定义min作为最短路径上节点的编号cos作为每条边上的权重
fori=0tonv循环里执行的操作是把每个于v有联系的节点加上权值
接下来初始化三个数组
fori=0tonv的循环操作是找出最短路径
forj=0tonv的循环操作是重复找出离当前节点最近的节点并且记录名称和对应的权值
再次初始化利用pri算法对路径进行修正
2.2.2 提交列表
2.2.3 本题知识点
Dijkstra找路径,图遍历,