《算法4》加权有向图最短路径算法(Dijkstra算法)

Dijkstra算法原理:s到w顶点,可以通过其他顶点中转到达并缩短距离,就松弛s到w的路径;

demo中使用的测试数据,来自《算法4》:

《算法4》配图里面的最短路径权重数据有小许误差,可自行手动验证看下:

用到的数据结构:
邻接边类 DirectedEdge.java

/**
 * 边的对象
 *
 * @author ltx
 */
public class DirectedEdge implements Comparable<DirectedEdge> {
    public int v;   //边的起点-顶点v
    public int w;   //边的终点-顶点w
    public double weight;  //权重

    public DirectedEdge(int v, int w, double weight) {
        this.v = v;
        this.w = w;
        this.weight = weight;
    }

    /**
     * 边的另外一个顶点
     *
     * @param v 顶点v
     * @return
     */
    public int other(int v) {
        if (this.v == v) {
            return this.w;
        } else if (this.w == v) {
            return this.v;
        }
        throw new RuntimeException("边不对!");
    }

    /**
     * 实现compareTo方法,集合排序用
     *
     * @param that
     * @return
     */
    @Override
    public int compareTo(DirectedEdge that) {
        if (this.weight > that.weight) {
            return 1;
        } else if (this.weight < that.weight) {
            return -1;
        }
        return 0;
    }

    @Override
    public String toString() {
        return String.format("%d->%d %.2f", v, w, weight);
    }
}

加权无向图类 DirectedEdgeWeightedGraph.java

/**
 * 加权有向图
 *
 * @author ltx
 */
public class DirectedEdgeWeightedGraph {
    public int V;  //顶点数量
    public int E;  //边数量
    public List<DirectedEdge>[] adj;  //邻接边

    /**
     * 初始化加权有向图
     *
     * @param V 顶点数
     */
    public DirectedEdgeWeightedGraph(int V) {
        //初始化顶点数
        this.V = V;
        //初始化边数
        this.E = 0;
        //初始化邻接表
        this.adj = new List[V];
        for (int i = 0; i < V; i++) {
            adj[i] = new ArrayList<>();
        }
    }

    /**
     * 添加边
     *
     * @param e 边
     */
    public void addEdge(DirectedEdge e) {
        adj[e.v].add(e);
        E++;
    }

    /**
     * 打印邻接表结构
     */
    public void show() {
        System.out.printf("%d 个顶点, %d 条边\n", V, E);
        for (int i = 0; i < V; i++) {
            System.out.println(i + ": " + adj[i]);
        }
    }

    /**
     * 初始化加权有向图
     *
     * @return
     */
    public static DirectedEdgeWeightedGraph init() {
        /**
         * 算法4里面的加权有向图
         */
        DirectedEdgeWeightedGraph directedEdgeWeightedGraph = new DirectedEdgeWeightedGraph(8);
        directedEdgeWeightedGraph.addEdge(new DirectedEdge(4, 5, 0.35));
        directedEdgeWeightedGraph.addEdge(new DirectedEdge(5, 4, 0.35));
        directedEdgeWeightedGraph.addEdge(new DirectedEdge(4, 7, 0.37));
        directedEdgeWeightedGraph.addEdge(new DirectedEdge(5, 7, 0.28));
        directedEdgeWeightedGraph.addEdge(new DirectedEdge(7, 5, 0.28));
        directedEdgeWeightedGraph.addEdge(new DirectedEdge(5, 1, 0.32));
        directedEdgeWeightedGraph.addEdge(new DirectedEdge(0, 4, 0.38));
        directedEdgeWeightedGraph.addEdge(new DirectedEdge(0, 2, 0.26));
        directedEdgeWeightedGraph.addEdge(new DirectedEdge(7, 3, 0.39));
        directedEdgeWeightedGraph.addEdge(new DirectedEdge(1, 3, 0.29));
        directedEdgeWeightedGraph.addEdge(new DirectedEdge(2, 7, 0.34));
        directedEdgeWeightedGraph.addEdge(new DirectedEdge(6, 2, 0.40));
        directedEdgeWeightedGraph.addEdge(new DirectedEdge(3, 6, 0.52));
        directedEdgeWeightedGraph.addEdge(new DirectedEdge(6, 0, 0.58));
        directedEdgeWeightedGraph.addEdge(new DirectedEdge(6, 4, 0.93));
        return directedEdgeWeightedGraph;
    }
}

顶点和权重类 Vertex.java
见上一章节《《算法4》加权无向图最小生成树算法(Prime算法(延时实现) | Prime算法(即时实现) | Kruskal)》

Dijkstra算法实现类 DijkstraSP.java

/**
 * 最短路径的Dijkstra算法
 * 原理-s到w顶点,可以通过其他顶点中转到达并缩短距离,就松弛s到w的路径
 *
 * @author ltx
 */
public class DijkstraSP {
    private DirectedEdgeWeightedGraph graph;
    private DirectedEdge[] edgeTo;  //实时动态存-顶点s到顶点w的最短路径的最后一条边
    private double[] distTo;    //实时动态存-顶点s到顶点w的最短路径权重和
    private List<Vertex> pq;    //顶点队列
    public List<DirectedEdge> sp;   //最短路径sp所有的边
    private int s; //起点s

    /**
     * s到v的距离,不存在则返回无穷大
     *
     * @param v 顶点
     * @return
     */
    public double distTo(int v) {
        System.out.printf("%d->%d的最短距离: \n", s, v);
        return distTo[v];
    }

    /**
     * 是否存在顶点s到顶点v的路径
     *
     * @param v 顶点
     * @return
     */
    public boolean hasPathTo(int v) {
        System.out.printf("是否存在%d->%d的路径: \n", s, v);
        return distTo[v] < Double.POSITIVE_INFINITY;
    }

    /**
     * s到v的最短路径,没有则返回null
     *
     * @param v 顶点
     * @return
     */
    public List<DirectedEdge> pathTo(int v) {
        System.out.printf("%d->%d的最短路径: \n", s, v);
        List<DirectedEdge> path = new ArrayList<>();
        Stack<DirectedEdge> st = new Stack<>();
        //最后一条路径
        DirectedEdge temp = edgeTo[v];
        //放最后一条路径
        st.push(temp);
        while (temp != null && temp.v != s) {
            //放中间的路径
            st.push(edgeTo[temp.v]);
            temp = edgeTo[temp.v];
        }
        //栈逆序到list,构成路径
        while (!st.isEmpty()) {
            path.add(st.pop());
        }
        return path;
    }

    /**
     * 初始化最短路径sp
     *
     * @param graph 加权有向图
     */
    public DijkstraSP(DirectedEdgeWeightedGraph graph, int s) {
        this.graph = graph;
        this.s = s;
        edgeTo = new DirectedEdge[graph.V];
        distTo = new double[graph.V];
        //将顶点v到顶点s的权重初始化为最大
        for (int i = 0; i < graph.V; i++) {
            //正无穷的常数
            distTo[i] = Double.POSITIVE_INFINITY;
        }
        pq = new LinkedList<>();
        sp = new ArrayList<>();
        //从顶点s开始
        distTo[s] = 0;
        pq.add(new Vertex(s, 0));
        while (!pq.isEmpty()) {
            //按权重排个序
            Collections.sort(pq);
            Vertex minV = pq.remove(0);
            //顶点松弛
            relax(minV.v);
        }
    }

    /**
     * 顶点松弛
     *
     * @param v 顶点v
     */
    private void relax(int v) {
        //检测顶点v的所有邻接边,并做松弛
        for (DirectedEdge e : graph.adj[v]) {
            int w = e.other(v);
            //起点s通过v顶点到达w顶点,比之前通过其他顶点到达顶点w距离的近,就做一次边松弛
            if (distTo[w] > distTo[v] + e.weight) {
                //v-w路径添加到路径数组中
                edgeTo[w] = e;
                //将起点s到顶点w的路径修改为当前最短(实时修改)
                distTo[w] = distTo[v] + e.weight;
                //如果队列里面有顶点w,则修改s到w的距离权重
                //Vertex重写了equals,可直接用w对比
                Vertex node = new Vertex(w, distTo[w]);
                if (pq.contains(node)) {
                    Vertex temp = pq.get(pq.indexOf(node));
                    temp.weight = e.weight;
                } else {
                    //没有就新增
                    pq.add(node);
                }
            }
        }
    }
}

测试方法:

@Test
    void sp() {
        System.out.println("################加权有向图################");
        DirectedEdgeWeightedGraph graph = DirectedEdgeWeightedGraph.init();
        graph.show();
        System.out.println("-------Dijkstra算法-------");
        DijkstraSP sp = new DijkstraSP(graph, 0);
        System.out.println(sp.hasPathTo(6));
        System.out.println(sp.pathTo(6));
        System.out.printf("%.2f\n", sp.distTo(6));
    }

测试结果:

################加权有向图################
8 个顶点, 15 条边
0: [0->4 0.38, 0->2 0.26]
1: [1->3 0.29]
2: [2->7 0.34]
3: [3->6 0.52]
4: [4->5 0.35, 4->7 0.37]
5: [5->4 0.35, 5->7 0.28, 5->1 0.32]
6: [6->2 0.40, 6->0 0.58, 6->4 0.93]
7: [7->5 0.28, 7->3 0.39]
-------Dijkstra算法-------
是否存在0->6的路径: 
true
0->6的最短路径: 
[0->2 0.26, 2->7 0.34, 7->3 0.39, 3->6 0.52]
0->6的最短距离: 
1.51

 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
### 回答1: 我能给你一些提示,但实际实现还是要靠你了。可以先考虑将所有节点和边的信息用一个数组或链表存储起来,接着从源节点开始,每次从未访问过的节点中,选择距离源节点最近的节点,更新最短路径,然后继续向下访问,直到抵达目标节点。 ### 回答2: Dijkstra算法是一种用于解决加权有向图中寻找最短路径的经典算法。下面是使用C语言实现Dijkstra算法的一个具体示例: ```c #include <stdio.h> #include <stdbool.h> #define INF 9999 #define V 6 // 用邻接矩阵表示有向图 int graph[V][V] = { {0, 4, 0, 0, 0, 0}, {0, 0, 6, 0, 2, 0}, {0, 0, 0, 1, 0, 0}, {7, 0, 0, 0, 0, 0}, {0, 0, 4, 0, 0, 4}, {3, 0, 0, 0, 0, 0} }; // 辅助函数,找到还未包含在最短路径集合中的最小的距离值 int minDistance(int distances[], bool shortestPathSet[]) { int min = INF, minIndex; for (int v = 0; v < V; v++) { if (shortestPathSet[v] == false && distances[v] <= min) { min = distances[v]; minIndex = v; } } return minIndex; } // 打印最短路径的函数 void printSolution(int distances[], int n) { printf("节点\t距离\n"); for (int i = 0; i < V; i++) { printf("%d\t%d\n", i, distances[i]); } } // Dijkstra算法的实现 void dijkstra(int source) { int distances[V]; bool shortestPathSet[V]; for (int i = 0; i < V; i++) { distances[i] = INF; shortestPathSet[i] = false; } distances[source] = 0; // 迭代V-1次,每次找到一个最短路径并加入最短路径集合 for (int count = 0; count < V - 1; count++) { int u = minDistance(distances, shortestPathSet); shortestPathSet[u] = true; for (int v = 0; v < V; v++) { if (!shortestPathSet[v] && graph[u][v] && distances[u] + graph[u][v] < distances[v]) { distances[v] = distances[u] + graph[u][v]; } } } // 打印最短路径结果 printSolution(distances, V); } int main() { int source = 0; // 设置源节点为0 dijkstra(source); return 0; } ``` 以上的C代码实现了Dijkstra算法,用于寻找给定加权有向图中从源节点到其他节点的最短路径。该代码通过邻接矩阵表示有向图,并提供了一个示例图供测试。函数`dijkstra()`实现了算法的主要逻辑,`minDistance()`函数用于找到距离集合最近的节点,`printSolution()`函数用于打印最短路径的结果。在`main()`函数中,可以设置源节点,并调用`dijkstra()`来计算最短路径。运行代码后,将输出每个节点的最短路径距离。 ### 回答3: Dijkstra算法是一种利用贪心策略来寻找加权有向图最短路径算法。下面是用C代码实现Dijkstra算法的具体步骤: 1. 定义结构体: ``` #define INFINITY 99999 #define MAX_SIZE 100 typedef struct { int weight; // 边的权重 int start; // 起点 int end; // 终点 } Edge; typedef struct { int vertex; // 顶点 int shortest; // 顶点到起点的最短距离 } Vertex; ``` 2. 实现Dijkstra算法函数: ``` void dijkstra(int graph[MAX_SIZE][MAX_SIZE], int start, int size) { Vertex vertices[MAX_SIZE]; // 所有顶点 int visited[MAX_SIZE] = {0}; // 标记已访问的顶点 int count, current, i, distance; // 初始化顶点数组 for (i = 0; i < size; i++) { vertices[i].vertex = i; vertices[i].shortest = INFINITY; } // 设置起点的最短距离为0 vertices[start].shortest = 0; // 遍历所有顶点 for (count = 0; count < size - 1; count++) { // 选择当前最短距离的顶点 current = findShortest(vertices, visited, size); // 标记当前顶点为已访问 visited[current] = 1; // 更新其他顶点的最短距离 for (i = 0; i < size; i++) { distance = graph[current][i]; if (!visited[i] && distance != 0 && vertices[current].shortest + distance < vertices[i].shortest) { vertices[i].shortest = vertices[current].shortest + distance; } } } // 输出最短路径 printf("最短路径为:\n"); for (i = 0; i < size; i++) { printf("%d -> %d: %d\n", start, i, vertices[i].shortest); } } ``` 3. 实现辅助函数:寻找当前顶点数组中最短距离的顶点 ``` int findShortest(Vertex vertices[], int visited[], int size) { int i, shortestDistance = INFINITY, shortestVertex; for (i = 0; i < size; i++) { if (!visited[i] && vertices[i].shortest < shortestDistance) { shortestDistance = vertices[i].shortest; shortestVertex = i; } } return shortestVertex; } ``` 4. 主函数中调用Dijkstra算法函数,并传入图的邻接矩阵和起点: ``` int main() { int graph[MAX_SIZE][MAX_SIZE] = {{0, 4, 0, 0, 0, 0, 0, 8, 0}, {4, 0, 8, 0, 0, 0, 0, 11, 0}, {0, 8, 0, 7, 0, 4, 0, 0, 2}, {0, 0, 7, 0, 9, 14, 0, 0, 0}, {0, 0, 0, 9, 0, 10, 0, 0, 0}, {0, 0, 4, 14, 10, 0, 2, 0, 0}, {0, 0, 0, 0, 0, 2, 0, 1, 6}, {8, 11, 0, 0, 0, 0, 1, 0, 7}, {0, 0, 2, 0, 0, 0, 6, 7, 0}}; int start = 0; int size = MAX_SIZE; dijkstra(graph, start, size); return 0; } ``` 以上就是用C代码实现Dijkstra算法寻找加权有向图最短路径的具体步骤。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

小小绿豆

你的鼓励是我创作最大的动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值