DS博客作业04--图
2020-05-05 17:35 pluto1214 阅读(215) 评论(0) 编辑 收藏 举报0.PTA得分截图
1.本周学习总结
1.1 总结图内容
图的存储结构:邻接表和邻接矩阵
邻接矩阵及其特点
1.无向图的邻接矩阵是对称的,有向图的可能是不对称的
2.对角线上的点的值都为0
3.时间复杂度和空间复杂度都为O(n^2)
4.在无向图中,统计第i行(列)1的个数可得顶点i的度。
在有向图中,统计第i行1的个数可得顶点i的出度OD;统计第j列1的个数可得顶点j的入度ID;
5.一个图的邻接矩阵是唯一的
邻接表及其特点
1.每条链上的其他节点都与头节点相连
2.下边为i的元素表示顶点为i的表头节点
3.图的邻接表存储方法是一种顺序分配与链式分配相结合的存储方法。
4.一个图的邻接表是不唯一的
邻接表存储类型定义: 邻接矩阵存储类型:
typedef struct ANode typedef struct
{ {
int adjvex; //该边的终点边号 int edges[MAXV][MAXV];//邻接矩阵
struct ANode*nextarc; //指向下条边的指针 int n,e;
int info; //权值 }AdjGraph;
}ArcNode;
typedef struct Vnode
{
int data;//顶点
ArcNode* firstarc;//指向第一条边
}VNode;
typedef VNode Adjlist[MAXV];
typedef struct
{
Adjlist adjlist;//邻接表
int n,e;
}AdjGraph;
图的遍历及其应用
深度遍历
含义:首先访问初始顶点v,选择一个与初始顶点相邻且没被访问过的顶点w,再从w进行深度搜索,直到所有顶点都被访问
邻接表深度遍历 邻接矩阵深度遍历
void DFS(AdjGraph* G, int v) void DFS(MGraph g, int v)
{ {
ArcNode* p; visited[v]=1;
visited[v] = 1; cout<<v;
cout<<v;
for(int j=1;j<=g.n;j++)
p = G->adjlist[v].firstarc; if(g.edges[v][j]!=0&&visited[j]==0)
while (p != NULL) DFS(g,j);
{ }
if (visited[p->adjvex] == 0)
DFS(G, p->adjvex);
p = p->nextarc;
}
}
判断从u到v是否有路径 输出从u到v的路径
void ExistPath(AGraph*G,int u,int v,bool&has) void FindaPath(AGraph*G,int u ,int v,int path[],int d)
{ {
ArcNode*p; ArcNode*p;int i;
visited[u]=1; visited[u]=1;d++;
if(u==v) path[d]=u;
{ if(u==v)
has=true; {
return; cout<<"一条路径为:";
} for(i=0;i<=d;i++)
p=G.adjlist[u].firstarc; cout<<path[i];
while(p!=NULL) return;
{ }
if(visited[p->adjvex]==0) p=G.adjlist[u].firstarc;
ExistPath(G,p->adjvex,v,has); while(p!=NULL)
p=p->nextarc; {
} if(visited[p->adjvex]==0)
} ExistPath(G,p->adjvex,v,path,d);
p=p->nextarc;
}
}
最短路径 判断图是否连通
void ShortPath(AdjGraph *G,int u,int v) int judge(AGraph g)
{ {
qu[rear].data=u;//第一个顶点u进队 for(int i=0;i<g.n;i++)
while (front!=rear)//队不空循环 visited[i]=0;
{ front++; DFS(G,0)
w=qu[front].data; for(int i=0;i<g.n;i++)
if (w==v) 根据parent关系输出路径break; if(!visited[i])//有节点没访问
while(遍历邻接表) return 0;
{ rear++; }
qu[rear].data=p->adjvex;
qu[rear].parent=front;
}
}
}
最小生成树相关算法及应用
概念:一个连通图的生成树是一个极小连通子图,它含有图中全部n个顶点和构成一棵树的(n-1)条边。不能回路。
prime算法求最小生成树
(1)初始化U={v}。v到其他顶点的所有边为候选边;
(2)重复以下步骤n-1次,使得其他n-1个顶点被加入到U中:
1.从候选边中挑选权值最小的边输出,设该边在V-U中的顶点是k,将k加入U中;
2.考察当前V-U中的所有顶点j,修改候选边:若(j,k)的权值小于原来和顶点k关联的候选边,则用(k,j)取代后者作为候选边。
克鲁斯卡尔算法求最小生成树
(1)置U的初值等于V(即包含有G中的全部顶点),TE的初值为空集(即图T中每一个顶点都构成一个连通分量)。
(2)将图G中的边按权值从小到大的顺序依次选取:
1.若选取的边未使生成树T形成回路,则加入TE;
2. 否则舍弃,直到TE中包含(n-1)条边为止。
Kruskal算法
最短路径相关算法及应用
用迪杰斯特拉(Dijkstra)算法求最短路径
初始化dist数组、path数组、s数组
遍历图中所有节点
{
for(i=0;i<g.n;i++) //找最短dist
{
若s[i]!=0,则dist数组找最短路径,顶点为u
}
s[u]=1 //加入集合S,顶点已选
for(i=0;i<g.n;i++) //修正dist
{
若s[i]!=0 && dist[i]>dist[u]+g.edges[u][i]
则修正dist[i]= dist[i]>dist[u]+g.edges[u][i]
path[i]=u;
}
}
1.从T中选取一个其距离值为最小的顶点W, 加入S
2.S中加入顶点w后,对T中顶点的距离值进行修改:
若加进W作中间顶点,从V0到Vj的距离值比不加W的路径要短,则修改此距离值;
3.重复上述步骤1,直到S中包含所有顶点,即S=V为止。
图像表示如下
用佛洛依德(Floyd)算法求最短路径
void Floyd(Graph g)
{
建立A数组和path数组,A[MAXV][MAXV],path[MAXV][MAXV];
for i=0 to g.n;
for j=0 to g.n
{
A和path数组初始化;
}
for k=0 to g.n
for i=0 to g.n
for j=0 to g.n
if(A[i][j]>A[i][k]+A[k][j])//找到更短路径
{
修改路径长度;
path[i][j]=k;
}
}
拓扑排序、关键路径
拓扑排序:在一个有向图中找一个拓扑序列的过程称为拓扑排序。
1.从有向图中选取一个没有前驱的顶点,并输出之;
2.从有向图中删去此顶点以及所有以它为尾的弧;
3.重复上述两步,直至图空,或者图不空但找不到无前驱的顶点为止。
typedef struct
{
int data;
int count; //存放顶点入度
ArcNode* firstarc;
}VNode;
void TopSort(AdjGraph*G)
{
遍历邻接表
计算每个顶点的入度,存入头结点count成员
遍历图顶点
若发现入度为0顶点,入栈st
while(栈不空)
{
出栈节点v,访问。
遍历v的所有邻接点
{
所有邻接点的入度-1
若有邻接点入度为0,则入栈st
}
}
}
void topSort(AdjMGraph* G) //拓扑排序算法
{
int i,j;
int St[MAXV];
int top=-1;
ArcNode*p;
for(i=0;i<G.n;i++)
G->adjlist[i].count=0;
for(i=0;i<G.n;i++) //求顶点入度
{
p=G->adjlist[i].firstarc;
while(p!=NULL)
{
G->adjlist[p->adjvex]++;
p=p->nextarc;
}
}
for(i=0;i<G.n;i++) //将入度为0的进栈
if(G->adjlist[i].count==0)
{
top++;
St[top]=i;
}
while(top>-1)
{
i=St[top];
top--;
cout<<i;
p=G->adjlist[i].firstarc;
while(p!=NULL)
{
j=p->adjvex;
G->adjlist[j].count--;//出边邻接点的入度减1
if(G->adjlist[j].count==0)//减1后若入度为0进栈
{
top++;
St[top]=j;
}
p=p->nextarc;
}
}
}
关键路径
1.关键路径为原点到汇点的最长路径
2.ve为事件的最早开始时间,vl为事件的最迟开始时间
过程如下:
- 求拓扑序列
- 按拓扑序列求ve数组
- 按拓扑序列和ve数组求vl数组
- 计算边的最早和最迟时间,即e和l
- 找e=l边即为关键活动,边连起来即为关键路径
1.2.谈谈你对图的认识及学习体会。
- 图需要记住的专业术语比较多,如连通分量,图,有向图和无向图等
- 图的算法很多,而且难度也挺大的,代码量都比较大
- 图的存储结构也是目前学的最复杂的了,邻接表的存储结构需要多个结构体
- 感觉图的编程题比较更接近实际生活问题了,例如村村通和旅游规划,这类题目实际性强,比较有趣
- 图的代码比较难理解,需要在编程过程中理解,看是很难记住的
2.阅读代码
2.1 题目及解题代码
2.1.1 该题的设计思路
用二维数组dp来存储答案,利用小机器人只能往右或者往下,那么dp[i][j]的值就是第 i 行第 j 列这个格子的上面那
个格子的值加上左边那个格子的值,也就是dp[i][j] = dp[i-1][j] + dp[i][j-1],因为这两个格子都可以走到
dp[i][j]这个格子,那么他们的路径数之和就是dp[i][j]的值。
2.1.2 该题的伪代码
2.1.3 运行结果
运行结果与题目所给答案一致
2.1.4分析该题目解题优势及难点。
解题优势:利用数学规律和题目所给的机器人行走方式进行解题,利用当前的路径条数等于左边格子的路径加上边格子的路径
即dp[i][j] = dp[i-1][j] + dp[i][j-1],将复杂问题通过规律简化
难点:解题思路很难,因为如果直观地要求到达指定格子的路径条数是非常复杂的,格子越多越复杂,因此只能想办法找出规律
2.2 题目及解题代码
2.2.1 该题的设计思路
从每个小岛出发,周围的格子距离加1,最后到达的海域的距离即为所求
2.2.2 该题的伪代码
2.2.3 运行结果
2.2.4分析该题目解题优势及难点。
解题优势:思路比较好理解,运用队列来辅助搜索
难点:对于图的遍历要非常熟悉,要灵活运用广度搜索
2.3 题目及解题代码
2.3.1 该题的设计思路
1.使用Floyd算法求出各个城市到其它城市的距离,保存在矩阵D[n][n]中。
2.遍历D[n][n],统计各个城市在距离不超过 distanceThreshold 的情况下,能到达的其它城市的数量。
3.返回能到达其它城市最少的城市
2.3.2 该题的伪代码
2.3.3 运行结果
2.3.4分析该题目解题优势及难点。
解题优势:运用Floyd算法求出最短路径来进行解题
难点:题目意思比较难懂,需要比较好的逻辑思维和对Floyd算法掌握的比较熟悉