枯木逢春不再茂,年少且惜镜边人
今天学习了Floyd 和 dijkstra算法 主要是两种理想的学习
但是在学习这两种思想的时候非常容易和之前学过的prime 和kruskal算法搞混!
所以 接下来复习prime 和kruskal算法
多的不说 直接上代码 具体看代码注释 !!!
*=====>floyd算法*
#include<iostream>
using namespace std;
typedef struct floyd{
int map[100][100]; //存储邻接矩阵
int path[100][100]; //Path存储 从 I - J 需要通过某个中介点 如果i-j直达则 path存储 i 没有直达也没有中介点相连则 为-1
int pa[100]; //用来最后输出路径
int vexnums; //存储图的节点个数
int rodenums; //存储路径条数
}FD;
void init(FD &f) { //初始化
for (int i = 0; i < f.vexnums; ++i) {
for (int j = 0; j < f.vexnums; ++j) {
if (i==j) { //如果I==J则 MAP = 0 path为I
f.map[i][j] = 0;
f.path[i][j] = i;
}else { //否则 map为999999 而Path则初始化为没有任何路径 -1
f.map[i][j]=999999;
f.path[i][j]=-1;
}
}
}
}
void FLD(FD &f){
for (int i = 0; i < f.vexnums; ++i) {
for (int j = 0; j < f.vexnums; ++j) {
for (int k = 0; k < f.vexnums; ++k) {
if (f.map[j][i]!=999999&&f.map[i][k]!=999999&&f.map[j][k]>f.map[j][i]+f.map[i][k]){
f.map[j][k]=f.map[j][i]+f.map[i][k];
f.path[j][k]=f.path[i][k];
}
}// if 里面的条件是FLOYD的核心 也就是map[j][k]>map[j][i]+map[i][k]
}//其中它的隐式条件是I!= J I!=K J!=K
}
}//floyd结束
void print(FD &f,int start ,int end){ //打印函数 有个技巧
int i=0,temp=end; //先把终点给temp
while (temp!=start){ //判断temp是不是起点
f.pa[i++]=temp; //不是起点如果是终点先加入路径 也可能是start ---end 的中介点 先加入路径
temp=f.path[start][temp]; //然后判断start ---temp 的中介点
}//继续判断 知道找到start 和temp直达的那条边 这也就是path矩阵的存储遍历
f.pa[i++]=start; //把start入到pa中去
cout<<start<<"到"<<end<<"的最短路径是 : "<<endl;
for (int j = i-1; j >0 ; --j) { //开始遍历
cout<<f.pa[j]<<"->";
}
cout<<f.pa[0]<<endl;
}
int main(){
FD f;
int start,end,len;
cout<<"请输入路径上的节点数和边数"<<endl;
cin>>f.vexnums>>f.rodenums;
init(f);
cout<<"请输入每条路径的 起始点 终点 和 权值 "<<endl;
for (int i = 0; i < f.rodenums; ++i) { //创建邻接矩阵
cin>>start>>end>>len;
f.map[start][end]=len;
f.map[end][start]=len;
f.path[start][end]=start;
f.path[end][start]=end;
}
cout<<"请输入起点和终点!"<<endl;
cin>>start>>end;
FLD(f);
if (f.map[start][end]==999999){
cout<<"-1"<<endl;
}else{
cout<<f.map[start][end]<<endl;
print(f,start,end);
}
}
测试结果
请输入路径上的节点数和边数
5 5
请输入每条路径的 起始点 终点 和 权值
0 1 2
1 2 2
2 3 3
3 4 4
1 4 5
请输入起点和终点!
0 4
7
0到4的最短路径是 :
0->1->4
具体 Path如何遍历 可以看看下图
下面就是Dijkstra的算法实现
请看代码
#include<iostream>
#include <limits>
using namespace std;
struct edgnode{ //边节点的结构体
int adjindex; //改变节点的下标
int weight; // 权值
edgnode *next;
};
struct Head{
int nodeinfo; //节点信息
int indegree; //节点入度
int value; //当前到起点的权值
bool change; //当前节点是否被选
int parent; //当前节点的双亲
edgnode *firstnode;
};
void creatnextlist(Head *G,int vexnums,int edgnums){ //创建邻接表
for (int i = 0; i < vexnums; ++i) {
G[i].nodeinfo=i+1; //使Vi 从v1开始
G[i].indegree=0; //入度 为0 该算法中无用
G[i].firstnode=NULL;
}
cout<<"请输入边的起点 终点 和 权值"<<endl;
for (int i = 0; i < edgnums; ++i) { //类似头插法建立邻接矩阵 有向网
int start ,end ,weight;
cin>>start>>end>>weight;
edgnode *node=new edgnode;
node->adjindex=end-1;
node->weight=weight;
node->next=G[start-1].firstnode;
G[start-1].firstnode=node;
}
}
int getweight(Head *G,int start ,int end){ //获得 start --end 的权值
edgnode *node =G[start-1].firstnode; //注意下标和实际数字的区别
while (node){
if (node->adjindex==end-1){ //找到start 直连的那个边 获得其权值
return node->weight;
}
node = node->next; //继续找
}
}
void dijkstra(Head *G,int vexnums,int start){ //算法开始
for (int i = 0; i < vexnums; ++i) {
G[i].value=INT_MAX; //一开始都没有路径连接 所以设置最大值
G[i].change=false; //全部设置为未选
}
G[start-1].value=0; //起点 初始化
G[start-1].parent=-1;
while (true){
bool flag= true; //设置flag 判断节点是否全部被选
for (int i = 0; i < vexnums; ++i) {
if (!G[i].change){ //没有被全部选中则就继续选择节点 flag=false
flag= false;
break;
}
}
if (flag){ //如果全部选中 则表示选择结束 算法结束直接返回
return ;
}
int min=-1; //每次遍历结构体 找到到起点权值最小的节点
for (int i = 0; i < vexnums; ++i) {
if (!G[i].change){
if (min==-1){
min=i;
}else{
if (G[min].value>G[i].value){
min=i;
}
}
}
} //找到后 min为找到的那个
cout<<"当前选中节点是V"<<min+1<<endl;
G[min].change=true; //标记选中
edgnode *node= G[min].firstnode; //开始找他直达的边
while (node){
int begin = min+1; //G[min].nodeinfo+1
int end = node->adjindex+1;
int weight =getweight(G,begin,end); //得到权值
if (weight+G[min].value<G[node->adjindex].value){ //判断起点到当前节点的路径长度是不是小于之前找的路径长度(直接更新) 或是之前没有找到 则直接赋值
G[node->adjindex].value=weight+G[min].value; //更新该值
G[node->adjindex].parent=min;//记录父亲 方便以后遍历用
}
node=node->next;
}
}
}
void print(Head *G , int k){ //利用递归的思想遍历 因为从最后查 父亲 然后 就 先查出的后输出
if (G[k-1].parent==-1){ //找到起点了 开始输出
cout<<"v"<<k;
}else if(k!=0){
print(G,G[k-1].parent+1); //K不是起点 则继续找他的父亲
cout<<" ->v"<<k;
}
}
int main(){
Head *G;
int vexnums,edgnums;
cout<<"请输入节点的个数 和 边数"<<endl;
cin>>vexnums>>edgnums;
G = new Head[vexnums]; //结构体数组
creatnextlist(G,vexnums,edgnums);
dijkstra(G,vexnums,1);
for (int i = 2; i <=vexnums ; ++i) {
cout<<"v1到V"<<i<<"的最短路径长度是"<<G[i-1].value<<endl;
print(G,i);
cout<<endl;
}
};
运行结果
请输入节点的个数 和 边数
5 5
请输入边的起点 终点 和 权值
1 2 2
2 3 2
3 4 3
4 5 4
2 5 5
当前选中节点是V1
当前选中节点是V2
当前选中节点是V3
当前选中节点是V4
当前选中节点是V5
v1到V2的最短路径长度是2
v1 ->v2
v1到V3的最短路径长度是4
v1 ->v2 ->v3
v1到V4的最短路径长度是7
v1 ->v2 ->v3 ->v4
v1到V5的最短路径长度是7
v1 ->v2 ->v5
Process finished with exit code 0