mirror of
https://github.com/Estom/notes.git
synced 2026-04-05 20:07:45 +08:00
图算法
This commit is contained in:
@@ -14,16 +14,19 @@
|
||||
|
||||
|
||||
## 1 问题分析
|
||||
最短路径算法。用于计算一个节点到其他节点的最短路径。
|
||||
* 最短路径算法。用于**计算一个节点到其他节点的最短路径。**
|
||||
|
||||
Dijkstra算法算是贪心思想实现的,首先把起点到所有点的距离存下来找个最短的,然后松弛一次再找出最短的,所谓的松弛操作就是,遍历一遍看通过刚刚找到的距离最短的点作为中转站会不会更近,如果更近了就更新距离,这样把所有的点找遍之后就存下了起点到其他所有点的最短距离。
|
||||
* Dijkstra算法算是**贪心思想**实现的,首先把起点到所有点的距离存下来找个最短的,然后松弛一次再找出最短的,所谓的松弛操作就是,遍历一遍看通过刚刚找到的距离最短的点作为中转站会不会更近,如果更近了就更新距离,这样把所有的点找遍之后就存下了起点到其他所有点的最短距离。
|
||||
|
||||
## 2 算法原理
|
||||
通过Dijkstra计算图G中的最短路径时,需要指定起点s(即从顶点s开始计算)。
|
||||
* 通过Dijkstra计算图G中的最短路径时,需要指定起点s(即从顶点s开始计算)。
|
||||
|
||||
此外,引进两个集合S和U。S的作用是记录已求出最短路径的顶点(以及相应的最短路径长度),而U则是记录还未求出最短路径的顶点(以及该顶点到起点s的距离)。
|
||||
* 此外,引进两个集合S和U。S的作用是记录已求出最短路径的顶点(以及相应的最短路径长度),而U则是记录还未求出最短路径的顶点(以及该顶点到起点s的距离)。
|
||||
|
||||
初始时,S中只有起点s;U中是除s之外的顶点,并且U中顶点的路径是"起点s到该顶点的路径"。然后,从U中找出路径最短的顶点,并将其加入到S中;接着,更新U中的顶点和顶点对应的路径。 然后,再从U中找出路径最短的顶点,并将其加入到S中;接着,更新U中的顶点和顶点对应的路径。 ... 重复该操作,直到遍历完所有顶点。
|
||||
* 初始时,S中只有起点s;U中是除s之外的顶点,并且U中顶点的路径是"起点s到该顶点的路径"。
|
||||
* 然后,从U中找出路径最短的顶点,并将其加入到S中;
|
||||
* 接着,更新U中的顶点和顶点对应的路径。
|
||||
* 然后,再从U中找出路径最短的顶点,并将其加入到S中;接着,更新U中的顶点和顶点对应的路径。 ... 重复该操作,直到遍历完所有顶点。
|
||||
|
||||
## 3 算法步骤
|
||||
|
||||
@@ -103,10 +106,10 @@ MGraph::MGraph(int n,int e){
|
||||
vertexNum=n;
|
||||
arcNum=e;
|
||||
for(int i=0;i<vertexNum;i++)
|
||||
vertex[i]=i;
|
||||
vertex[i]=i;
|
||||
for(int i=0;i<n;i++)
|
||||
for(int j=0;j<n;j++)
|
||||
arc[i][j]=10000;
|
||||
for(int j=0;j<n;j++)
|
||||
arc[i][j]=10000;
|
||||
cout<<"请输入图中各边的情况:"<<endl;
|
||||
for(int k=0;k<e;k++){
|
||||
cin>>i>>j;
|
||||
@@ -118,41 +121,41 @@ void Dijkstra(MGraph G,int v){
|
||||
string path[Max];
|
||||
for(int i=0;i<G.vertexNum;i++)
|
||||
{
|
||||
dist[i]=G.arc[v][i];
|
||||
if(dist[i]!=10000)
|
||||
path[i]=Int_to_String(G.vertex[v])+"->"+Int_to_String(G.vertex[i]);
|
||||
else
|
||||
path[i]="";
|
||||
dist[i]=G.arc[v][i];
|
||||
if(dist[i]!=10000)
|
||||
path[i]=Int_to_String(G.vertex[v])+"->"+Int_to_String(G.vertex[i]);
|
||||
else
|
||||
path[i]="";
|
||||
}
|
||||
s[0]=v;
|
||||
dist[v]=0;
|
||||
int num=1;
|
||||
for(int i=0;i<G.vertexNum;i++)
|
||||
{
|
||||
int t=10000,k;
|
||||
for(int j=0;j<G.vertexNum;j++)
|
||||
{
|
||||
if(dist[j]<t&&dist[j]!=0)
|
||||
int t=10000,k;
|
||||
for(int j=0;j<G.vertexNum;j++)
|
||||
{
|
||||
t=dist[j];
|
||||
k=j;
|
||||
if(dist[j]<t&&dist[j]!=0)
|
||||
{
|
||||
t=dist[j];
|
||||
k=j;
|
||||
}
|
||||
}
|
||||
}
|
||||
cout<<"终点为:"<<k<<" 最短路径长度为:"<<dist[k]<<" 过程:"<<path[k]<<endl;
|
||||
s[num++]=k;
|
||||
for(int j=0;j<G.vertexNum;j++){
|
||||
if(dist[j]!=0&&dist[j]>(dist[k]+G.arc[k][j])){
|
||||
dist[j]=dist[k]+G.arc[k][j];
|
||||
path[j]=path[k]+"->"+Int_to_String(j);
|
||||
cout<<"终点为:"<<k<<" 最短路径长度为:"<<dist[k]<<" 过程:"<<path[k]<<endl;
|
||||
s[num++]=k;
|
||||
for(int j=0;j<G.vertexNum;j++){
|
||||
if(dist[j]!=0&&dist[j]>(dist[k]+G.arc[k][j])){
|
||||
dist[j]=dist[k]+G.arc[k][j];
|
||||
path[j]=path[k]+"->"+Int_to_String(j);
|
||||
}
|
||||
}
|
||||
}
|
||||
dist[k]=0;
|
||||
if(num==G.vertexNum)
|
||||
break;
|
||||
dist[k]=0;
|
||||
if(num==G.vertexNum)
|
||||
break;
|
||||
}
|
||||
cout<<"找到终点的顺序为:"<<endl;
|
||||
for(int i=1;i<num;i++)
|
||||
cout<<s[i]<<" ";
|
||||
cout<<s[i]<<" ";
|
||||
cout<<endl;
|
||||
}
|
||||
int main(){
|
||||
|
||||
@@ -13,44 +13,33 @@
|
||||
> * [https://blog.csdn.net/jeffleo/article/details/53349825](https://blog.csdn.net/jeffleo/article/details/53349825)
|
||||
## 1 问题分析
|
||||
|
||||
Floyd算法是一个经典的**动态规划算法**,它又被称为插点法。该算法名称以创始人之一、1978年图灵奖获得者、斯坦福大学计算机科学系教授罗伯特·弗洛伊德命名。Floyd算法是一种利用动态规划的思想寻找给定的加权图中多源点之间最短路径的算法,算法目标是寻找从点i到点j的最短路径。
|
||||
* Floyd算法是一个经典的**动态规划算法**,它又被称为插点法。该算法名称以创始人之一、1978年图灵奖获得者、斯坦福大学计算机科学系教授罗伯特·弗洛伊德命名。Floyd算法是一种利用动态规划的思想寻找给定的加权图中多源点之间最短路径的算法,算法目标是寻找从点i到点j的最短路径。
|
||||
|
||||
## 2 算法原理
|
||||
|
||||
Floyd算法的基本思想:
|
||||
可以将问题分解:
|
||||
第一、先找出最短的距离
|
||||
第二、然后在考虑如何找出对应的行进路线。
|
||||
* Floyd算法的基本思想:可以将问题分解:
|
||||
* 第一、先找出最短的距离
|
||||
* 第二、然后在考虑如何找出对应的行进路线。
|
||||
|
||||
> 以后再整理一下文字内容
|
||||
|
||||
|
||||
如何找出最短路径呢,这里还是用到动态规划的知识,对于任何一个城市而言,i到j的最短距离不外乎存在经过i与j之间经过k和不经过k两种可能,所以可以令k=1,2,3,...,n(n是城市的数目),在检查d(ij)与d(ik)+d(kj)的值;在此d(ik)与d(kj)分别是目前为止所知道的i到k与k到j的最短距离,因此d(ik)+d(kj)就是i到j经过k的最短距离。所以,若有d(ij)>d(ik)+d(kj),就表示从i出发经过k再到j的距离要比原来的i到j距离短,自然把i到j的d(ij)重写为d(ik)+d(kj),每当一个k查完了,d(ij)就是目前的i到j的最短距离。重复这一过程,最后当查完所有的k时,d(ij)里面存放的就是i到j之间的最短距离了。
|
||||
|
||||
接下来就要看一看如何找出最短路径所行经的城市了,这里要用到另一个矩阵P,它的定义是这样的:p(ij)的值如果为p,就表示i到j的最短行经为i->...->p->j,也就是说p是i到j的最短行径中的j之前的最后一个城市。P矩阵的初值为p(ij)=i。有了这个矩阵之后,要找最短路径就轻而易举了。对于i到j而言找出p(ij),令为p,就知道了路径i->...->p->j;再去找p(ip),如果值为q,i到p的最短路径为i->...->q->p;再去找p(iq),如果值为r,i到q的最短路径为i->...->r->q;所以一再反复,到了某个p(it)的值为i时,就表示i到t的最短路径为i->t,就会的到答案了,i到j的最短行径为i->t->...->q->p->j。因为上述的算法是从终点到起点的顺序找出来的,所以输出的时候要把它倒过来。
|
||||
|
||||
但是,如何动态的回填P矩阵的值呢?回想一下,当d(ij)>d(ik)+d(kj)时,就要让i到j的最短路径改为走i->...->k->...->j这一条路,但是d(kj)的值是已知的,换句话说,就是k->...->j这条路是已知的,所以k->...->j这条路上j的上一个城市(即p(kj))也是已知的,当然,因为要改走i->...->k->...->j这一条路,j的上一个城市正好是p(kj)。所以一旦发现d(ij)>d(ik)+d(kj),就把p(kj)存入p(ij)。
|
||||
|
||||
## 3 算法过程
|
||||

|
||||
|
||||

|
||||
> 顶点名称和下标的对应
|
||||
> * A B C D E F G
|
||||
> * 0 1 2 3 4 5 6
|
||||
|
||||
1. 定义n×n的方阵序列D-1, D0 , … Dn-1,
|
||||
1. 弗洛伊德算法定义了两个二维矩阵
|
||||
* 矩阵D记录顶点间的最小路径。例如D[0][3]= 10,说明顶点0 到 3 的最短路径为10;
|
||||
* 矩阵P记录顶点间最小路径中的中转点
|
||||
* 例如P[0][3]= 1 说明,0 到 3的最短路径轨迹为:0 -> 1 -> 3。
|
||||
|
||||
2. 初始化: D-1=C
|
||||
D-1[i][j]=边<i,j>的长度,表示初始的从i到j的最短路径长度,即它是从i到j的中间不经过其他中间点的最短路径。
|
||||
2. 它通过3重循环,k为中转点,v为起点,w为终点,循环比较D[v][w] 和 D[v][k] + D[k][w] 最小值,如果D[v][k] + D[k][w] 为更小值,则把D[v][k] + D[k][w] 覆盖保存在D[v][w]中。
|
||||
|
||||
3. 迭代:设Dk-1已求出,如何得到Dk(0≤k≤n-1)?
|
||||
3. 以A为中间点,原D矩阵中,D[B][G]的值为INF,即不存在B->G的最小路径,但是通过A为中间点,D[B][A] + D[A][G] = 12 + 14 = 26 小于 D[B][G] = INF, 所以D[B][A] + D[A][G] 为 B -> G的最小值,因此覆盖D[B][G] 为 26。
|
||||
|
||||
* Dk-1[i][j]表示从i到j的中间点不大于k-1的最短路径p:i…j,
|
||||
* 考虑将顶点k加入路径p得到顶点序列q:i…k…j,
|
||||
* 若q不是路径,则当前的最短路径仍是上一步结果:Dk[i][j]= Dk-1[i][j];
|
||||
* 否则若q的长度小于p的长度,则用q取代p作为从i到j的最短路径
|
||||
|
||||
4. 因为q的两条子路径i…k和k…j皆是中间点不大于k-1的最短路径,所以从i到j中间点不大于k的最短路径长度为:
|
||||
$$
|
||||
Dk[i][j]=min\{ Dk-1[i][j], Dk-1[i][k] +Dk-1[k][j]\}
|
||||
$$
|
||||
4. 以B为中间点,第2步后的D矩阵中,D[A][C]的值为INF, 但是通过B,D[A][B] + D[B][C] = 12 + 10 = 22 小于 D[A][C] = INF,所以D[A][B] + D[B][C] 为 A->C的最小路径,覆盖D[A][C]的值为22, 以此类推。
|
||||
|
||||
## 4 算法效率
|
||||
|
||||
@@ -59,6 +48,23 @@ $$
|
||||
## 5 算法实现
|
||||
|
||||
```
|
||||
//这里是弗洛伊德算法的核心部分
|
||||
//k为中间点
|
||||
for(k = 0; k < G.vexnum; k++){
|
||||
//v为起点
|
||||
for(v = 0 ; v < G.vexnum; v++){
|
||||
//w为终点
|
||||
for(w =0; w < G.vexnum; w++){
|
||||
if(D[v][w] > (D[v][k] + D[k][w])){
|
||||
D[v][w] = D[v][k] + D[k][w];//更新最小路径
|
||||
P[v][w] = P[v][k];//更新最小路径中间顶点
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
```C++
|
||||
#include<iostream>
|
||||
#include<string.h>
|
||||
using namespace std;
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
> 参考文献
|
||||
> [https://www.cnblogs.com/ggzhangxiaochao/p/9070873.html](https://www.cnblogs.com/ggzhangxiaochao/p/9070873.html)
|
||||
## 1 问题描述
|
||||
Kruskal算法是一种用来寻找最小生成树的算法,由Joseph Kruskal在1956年发表。用来解决同样问题的还有Prim算法和Boruvka算法等。三种算法都是贪婪算法的应用。和Boruvka算法不同的地方是,Kruskal算法在图中存在相同权值的边时也有效。
|
||||
* Kruskal算法是一种用来寻找最小生成树的算法,由Joseph Kruskal在1956年发表。用来解决同样问题的还有Prim算法和Boruvka算法等。三种算法都是贪婪算法的应用。和Boruvka算法不同的地方是,Kruskal算法在图中存在相同权值的边时也有效。
|
||||
|
||||
## 2 算法原理
|
||||
|
||||
@@ -50,7 +50,7 @@ Kruskal算法是一种用来寻找最小生成树的算法,由Joseph Kruskal
|
||||
|
||||
## 5 算法实现
|
||||
|
||||
```
|
||||
```C++
|
||||
typedef struct
|
||||
{
|
||||
char vertex[VertexNum]; //顶点表
|
||||
|
||||
@@ -115,7 +115,7 @@
|
||||
|
||||
## 5 算法实现
|
||||
|
||||
```
|
||||
```C++
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#define VertexType int
|
||||
|
||||
BIN
算法/B类:数据结构算法/image/2021-03-13-22-44-58.png
Normal file
BIN
算法/B类:数据结构算法/image/2021-03-13-22-44-58.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 465 KiB |
Reference in New Issue
Block a user