算法——有向图的最短路径算法

建议学习最短路径算法时,观看这个视频

https://www.bilibili.com/video/BV1q4411M7r9?from=search&seid=9662298119837732890 

Dijkstra算法

思路:

1.从一个单源节点开始计算,寻找当前节点周围没有遍历过并且可到达的邻接点,并且把当前单源节点设置为以访问状态;

2.如果从当前节点到这些邻接点的距离更短,那么更新这些邻接点的父节点数组为当前节点,距离数组为当前节点的距离数组+这些邻接点与当前节点之间弧的权重;

3.并且在遍历当前节点的邻接点的时候,要选出当前节点与这些邻接点之间弧权重最短的那个邻接点作为下一次的单源节点;

4.对下一次的单元节点的操作与上面操作类似;

5.以上操作需要借助队列来实现。

6.关于有向图还要注意i->j的弧,只能由i移动到j

//有向图
#include<iostream>
#include<vector>
#include<queue>
#include<climits>
using namespace std;
//边表节点
typedef struct ArcNode
{
    int Adjvex;
    int wet;
    ArcNode*next=nullptr;
    ArcNode(int x,ArcNode*p,int y):Adjvex(x),next(p),wet(y) {}
} ArcNode;
//顶点表节点
typedef struct VerNode
{
    int Vervex;
    ArcNode*First_Edge;
    VerNode(int x,ArcNode*p):Vervex(x),First_Edge(p) {}
} VerNode;
class d_chart
{
private:
    vector<VerNode*>VerNode_List;
    vector<int>Visited_Mark;
    vector<int>Parent;//存储节点的上一个父亲节点
    vector<int>Distance;//存储源节点到每一个节点的最短距离
public:
    d_chart() {}
    void make(vector<int>data,int Road_Nums);
    void Print();
    void Reset();
    void Dijkstra(int u);
    ~d_chart();
};
void d_chart::make(vector<int>data,int Road_Nums)
{
    VerNode*Node_Pointer_temp;
    ArcNode*Arc_Pointer;
    int i,j,k,w;
    int Data_Len=data.size();
    for(i=0; i<Data_Len; i++)
    {
        Node_Pointer_temp=new VerNode(data[i],nullptr);
        VerNode_List.push_back(Node_Pointer_temp);
        Visited_Mark.push_back(0);
        Parent.push_back(-1);
        Distance.push_back(INT_MAX);
    }
    for(k=0; k<Road_Nums; k++)
    {
        cout<<"请输入有向图的路径i——>j,以及弧的权重"<<endl;
        cout<<"请输入i:  ";
        cin>>i;
        cout<<endl<<"请输入j:  ";
        cin>>j;
        cout<<endl<<"请输入i->j弧的权重";
        cin>>w;
        cout<<endl;
        Arc_Pointer=new ArcNode(j,VerNode_List[i]->First_Edge,w);
        VerNode_List[i]->First_Edge=Arc_Pointer;
    }
}
void d_chart::Print()
{
    ArcNode*s;
    int i;
    for(i=0; i<VerNode_List.size(); i++)
    {
        //cout<<VerNode_List[i]->Vervex;
        s=VerNode_List[i]->First_Edge;
        while(s!=nullptr)
        {
            cout<<" "<<VerNode_List[i]->Vervex<<"——>"<<s->Adjvex<<" ";
            s=s->next;
        }
        cout<<endl;
    }
}
d_chart::~d_chart()
{
    ArcNode*s;
    ArcNode*q;
    int i;
    for(i=0; i<VerNode_List.size(); i++)
    {
        s=VerNode_List[i]->First_Edge;
        delete VerNode_List[i];
        while(s!=nullptr)
        {
            q=s;
            s=s->next;
            delete q;
        }
    }
}
void d_chart::Reset()
{
    int i=0;
    for(i=0; i<Visited_Mark.size(); i++)
    {
        Visited_Mark[i]=0;
        Parent[i]=-1;
        Distance[i]=INT_MAX;
    }
}
void d_chart::Dijkstra(int u)
{
    int node=Parent.size();//节点数
    queue<VerNode*> s;

    s.push(VerNode_List[u]);
    Parent[u]=-1;
    Distance[u]=0;

    while(!s.empty())
    {
        int idx=s.front()->Vervex;
        //cout<<"当前节点——"<<idx<<endl;
        Visited_Mark[idx]=1;
        int last=-1;//记录最后要入队的那个节点的下标
        int last_wt=INT_MAX;
        for(ArcNode*p=VerNode_List[idx]->First_Edge;p!=nullptr;p=p->next)
        {
            int pls=p->Adjvex;
            if(Visited_Mark[pls]==0)//没有被访问过
            {
                //cout<<"可以选择的节点****"<<pls<<endl;
                if(((p->wet)+Distance[idx])<Distance[pls])
                {
                    Distance[pls]=((p->wet)+Distance[idx]);
                    Parent[pls]=idx;
                }
                if((p->wet)<last_wt)
                {
                    last=pls;
                    last_wt=(p->wet);
                }
            }
        }
        //cout<<"最终选择的节点++++"<<last<<endl<<endl;
        if(last!=-1)//如果最终选中的节点下标等于-1,那么说明没有与当前节点相连的节点了
        {
            s.push(VerNode_List[last]);
        }
        s.pop();
    }
    cout<<endl<<"当前的源节点是"<<u<<endl;
    //打印出各个数组存储的数据

    cout<<"Distance数组:"<<endl;
    for(int i=0;i<node;i++)
    {
        cout<<Distance[i]<<" ";
    }
    cout<<endl<<"Parent数组:"<<endl;
    for(int i=0;i<node;i++)
    {
        cout<<Parent[i]<<" ";
    }
}
int main()
{
    vector<int> p={0,1,2,3,4};
    d_chart cool;
    cool.make(p,10);
    cool.Print();
    cool.Dijkstra(0);
    return 0;
}

运行:

说实在的,有向图的Dijkstra算法和无向图的算法步骤大同小异,基本一样。

Floyd算法

思路:

1.使用两个n*n的矩阵Dist,Plain(n是节点的个数),Disti节点到j节点的最短距离,Plain存放的是i到j最短路径的节点遍历顺序,

比如Plain[i][j]=k,那么从i到j的最短路径就要经过k节点,那么就去Plain[k][j]查看Plain[k][j]的值是否为j,如果为,那么最短路径就是

i->k->j,否则就继续像之前那样寻找最短路径上的节点。

 

2.初始换Dist数组,其实就是把邻接表转换成邻接矩阵,从而更好操作,主对角线由于图中弧权重可为负数,所以主对角线上可以初始化为一个极大或者极小的值,然后不相通的两个节点的Dist不能直接设置为INT_MAX,在这上面我吃了大亏,他会整型越界,最好设置为INT_MAX-50

 

3.初始化Plain数组,Plain[i][j]初始化为j,而当i==j的时候,初始化为-1,因为节点中没有-1为下标。

 

4.然后使用三层循环,最外层的k循环,是循环遍历中间节点,内两层循环更新距离数组,如果i先到k这个中间节点在到j节点比之前其他路径更短,那么同时更新路径数组,和顺序数组,很重要的一点在更新的时候,下标为k的行和列以及对角线不参与这种更新。

 

5.最后还有更加重要的一点就是如果i无法到达k的话,就不能更新,因为当k到j的弧的权重为负数的话就会发生非常不好的结果,我深有体会!

 

 

代码

//有向图
#include<iostream>
#include<vector>
#include<queue>
#include<climits>
using namespace std;
//边表节点
typedef struct ArcNode
{
    int Adjvex;
    int wet;
    ArcNode*next=nullptr;
    ArcNode(int x,ArcNode*p,int y):Adjvex(x),next(p),wet(y) {}
} ArcNode;
//顶点表节点
typedef struct VerNode
{
    int Vervex;
    ArcNode*First_Edge;
    VerNode(int x,ArcNode*p):Vervex(x),First_Edge(p) {}
} VerNode;
class d_chart
{
private:
    vector<VerNode*>VerNode_List;//节点
public:
    d_chart() {}
    void make(vector<int>data,int Road_Nums);
    void Print();
    void Floyd();
    ~d_chart();
};
void d_chart::make(vector<int>data,int Road_Nums)
{
    VerNode*Node_Pointer_temp;
    ArcNode*Arc_Pointer;
    int i,j,k,w;
    int Data_Len=data.size();
    for(i=0; i<Data_Len; i++)
    {
        Node_Pointer_temp=new VerNode(data[i],nullptr);
        VerNode_List.push_back(Node_Pointer_temp);
        Visited_Mark.push_back(0);
        Parent.push_back(-1);
        Distance.push_back(INT_MAX);
    }
    for(k=0; k<Road_Nums; k++)
    {
        cout<<"请输入有向图的路径i——>j,以及弧的权重"<<endl;
        cout<<"请输入i:  ";
        cin>>i;
        cout<<endl<<"请输入j:  ";
        cin>>j;
        cout<<endl<<"请输入i->j弧的权重";
        cin>>w;
        cout<<endl;
        Arc_Pointer=new ArcNode(j,VerNode_List[i]->First_Edge,w);
        VerNode_List[i]->First_Edge=Arc_Pointer;
    }
}
void d_chart::Print()
{
    ArcNode*s;
    int i;
    for(i=0; i<VerNode_List.size(); i++)
    {
        //cout<<VerNode_List[i]->Vervex;
        s=VerNode_List[i]->First_Edge;
        while(s!=nullptr)
        {
            cout<<" "<<VerNode_List[i]->Vervex<<"——>"<<s->Adjvex<<" ";
            s=s->next;
        }
        cout<<endl;
    }
}
d_chart::~d_chart()
{
    ArcNode*s;
    ArcNode*q;
    int i;
    for(i=0; i<VerNode_List.size(); i++)
    {
        s=VerNode_List[i]->First_Edge;
        delete VerNode_List[i];
        while(s!=nullptr)
        {
            q=s;
            s=s->next;
            delete q;
        }
    }
}
void d_chart::Floyd()
{
    int node=Parent.size();
    //Floyd算法所需数组
    vector<vector<int>>Dist(node,vector<int>(node,INT_MAX-50));//距离数组
    vector<vector<int>>Plain(node,vector<int>(node));//顺序数组

    //初始化顺序数组
    for(int i=0; i<node; i++)
        for(int j=0; j<node; j++)
        {
            if(i==j)
            {
                Plain[i][j]=-1;
            }
            else
            {
                Plain[i][j]=j;
            }
        }

    /*先打印初始化的顺序数组*/
    cout<<"——初始化的顺序数组——"<<endl;
    for(int i=0; i<node; i++)
    {
        for(int j=0; j<node; j++)
            cout<<Plain[i][j]<<"  ";
        cout<<endl;
    }
    //初始化距离数组
    for(int i=0; i<node; i++)
    {
        int x=VerNode_List[i]->Vervex;
        for(ArcNode*p=VerNode_List[i]->First_Edge; p!=nullptr; p=p->next)
        {
            int y=p->Adjvex;
            Dist[x][y]=p->wet;
        }
    }


    //距离数组对角线
    for(int i=0; i<node; i++)
        for(int j=0; j<node; j++)
        {
            if(i==j)
                Dist[i][j]=0;
        }
    /*打印初始化的距离数组*/
    cout<<"——初始化的距离数组——"<<endl;
    for(int i=0; i<node; i++)
    {
        for(int j=0; j<node; j++)
        {
            if(i==j)
            {
                cout<<"-  ";
                continue;
            }
            if(Dist[i][j]==(INT_MAX-50))//
                cout<<"∞"<<"  ";
            else
                cout<<Dist[i][j]<<"  ";
        }
        cout<<endl;
    }

    //三层循环
    for(int k=0; k<node; k++)
    {
        for(int i=0; i<node; i++)
        {
            if(i==k)
                continue;
            for(int j=0; j<node; j++)
            {
                if(j==k)
                    continue;
                if(i==j)
                    continue;

                /*cout<<"Dist["<<i<<"]["<<k<<"]: ";
                if(Dist[i][k]==INT_MAX-50)
                    cout<<"∞"<<"  ";
                else
                    cout<<Dist[i][k]<<"  ";
                cout<<"Dist["<<k<<"]["<<j<<"]:  ";
                if(Dist[k][j]==INT_MAX-50)
                    cout<<"∞"<<"  ";
                else
                    cout<<Dist[k][j]<<"  ";
                cout<<"Dist["<<i<<"]["<<j<<"]:  ";
                if(Dist[i][j]==INT_MAX-50)
                    cout<<"∞"<<"  ";
                else
                    cout<<Dist[i][j]<<"  ";
                    cout<<endl;*/



                if((Dist[i][k]+Dist[k][j])<Dist[i][j]&&(Dist[i][k]!=(INT_MAX-50)&&Dist[k][j]!=(INT_MAX-50)))//
                {
                    Dist[i][j]=(Dist[i][k]+Dist[k][j]);
                    Plain[i][j]=Plain[i][k];
                }
            }
        }
        cout<<"——中间节点为"<<k<<"之后距离矩阵的更新——"<<endl;
        for(int i=0; i<node; i++)
        {
            for(int j=0; j<node; j++)
            {
                if(i==j)
                {
                    cout<<"-  ";
                    continue;
                }
                if(Dist[i][j]==INT_MAX-50)
                    cout<<"∞"<<"  ";
                else
                    cout<<Dist[i][j]<<"  ";
            }
            cout<<endl;
        }

    }

    //打印距离数组
    cout<<"——距离数组——"<<endl;
    for(int i=0; i<node; i++)
    {
        for(int j=0; j<node; j++)
        {
            if(i==j)
            {
                cout<<"-  ";
                continue;
            }
            if(Dist[i][j]==INT_MAX-50)
                cout<<"∞"<<"  ";
            else
                cout<<Dist[i][j]<<"  ";
        }
        cout<<endl;
    }


    //打印顺序数组
    cout<<"——顺序数组——"<<endl;
    for(int i=0; i<node; i++)
    {
        for(int j=0; j<node; j++)
            cout<<Plain[i][j]<<"  ";
        cout<<endl;
    }
}
int main()
{
    vector<int> p= {0,1,2,3,4};
    d_chart cool;
    cool.make(p,9);
    cool.Print();
    cool.Floyd();
    return 0;
}

 

基本公式:

测试用图:


 

 

 

 

程序运行:

 

 

 

  • 2
    点赞
  • 18
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Dijkstra算法可以用来求带权有向图上的最短路径,下面是Java实现: ```java import java.util.*; public class DijkstraAlgorithm { private static final int MAX = Integer.MAX_VALUE; // 定义无穷大 public static void dijkstra(int[][] graph, int start) { int n = graph.length; // 的大小 int[] dist = new int[n]; // 存储起点到各个点的最短距离 boolean[] visited = new boolean[n]; // 标记各个结点是否已经访问过 int[] prev = new int[n]; // 存储到达各个结点的前驱结点 // 初始化 for (int i = 0; i < n; i++) { dist[i] = MAX; visited[i] = false; prev[i] = -1; } dist[start] = 0; // 循环n-1次,每次确定一个顶点的最短路径 for (int i = 0; i < n - 1; i++) { int minDist = MAX; int u = -1; // 找到当前未访问的结点中距离起点最近的结点 for (int j = 0; j < n; j++) { if (!visited[j] && dist[j] < minDist) { minDist = dist[j]; u = j; } } if (u == -1) { break; } visited[u] = true; // 更新与u相邻的结点的最短距离 for (int j = 0; j < n; j++) { if (!visited[j] && graph[u][j] != MAX) { int newDist = dist[u] + graph[u][j]; if (newDist < dist[j]) { dist[j] = newDist; prev[j] = u; } } } } // 输出结果 System.out.println("起点为" + start + "的最短路径如下:"); for (int i = 0; i < n; i++) { if (i != start && dist[i] != MAX) { System.out.print("从" + start + "到" + i + "的最短路径为:" + start); int j = i; while (j != start) { System.out.print(" -> " + j); j = prev[j]; } System.out.println(",路径长度为:" + dist[i]); } } } public static void main(String[] args) { int[][] graph = { {0, 1, 12, MAX, MAX}, {MAX, 0, 9, 3, MAX}, {MAX, MAX, 0, MAX, 5}, {MAX, MAX, 4, 0, 13}, {MAX, MAX, MAX, MAX, 0} }; dijkstra(graph, 0); } } ``` 运行结果: ``` 起点为0的最短路径如下: 从0到1的最短路径为:0 -> 1,路径长度为:1 从0到2的最短路径为:0 -> 1 -> 3 -> 2,路径长度为:12 从0到3的最短路径为:0 -> 1 -> 3,路径长度为:4 从0到4的最短路径为:0 -> 1 -> 3 -> 2 -> 4,路径长度为:17 ```

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值