图中的路径和最短路


title: 图中的路径和最短路‘
typora-root-url: 图中的路径和最短路‘
date: 2023-07-31 08:44:51
categories: 算法
tags: [算法,图论]



图中的路径和最短路

在上一节中,讲解了图的邻接表和邻接矩阵表示法。这一节来看看,图的路径与最小路问题

最短路径问题是图论中常见的一个问题,主要是要找到在图中两个节点之间的最短路径,其中路径的定义可以根据具体情况而定。

在无权图中,最短路径是指两个节点之间经过的边数量最少的路径。可以使用广度优先搜索算法(BFS)来解决这类问题,该算法从起始节点开始逐层扩展,直到找到目标节点。

在有权图中,每条边都有一个权重值,最短路径问题就变成了找到两个节点之间权重和最小的路径。可以使用迪杰斯特拉算法(Dijkstra’s algorithm)或A算法来解决这类问题。迪杰斯特拉算法从起始节点开始,逐步更新到达其他节点的最短距离,直到找到目标节点的最短路径。A算法是一种启发式搜索算法,结合了迪杰斯特拉算法和一定的启发式函数,能更高效地找到最短路径。

除了以上提到的两种算法,还有其他一些算法可以用于解决最短路径问题,具体算法的选择取决于图的特点和需求。

无权图的最短路径算法

对于无权图的最短路径问题,可以使用广度优先搜索算法(BFS)来求解。以下是使用BFS算法解决无权图最短路径问题的基本步骤:

  1. 选择一个起始节点作为搜索的起点。

  2. 将起始节点加入到一个队列中,并将其标记为已访问。

  3. 循环执行以下步骤直到队列为空:

    a. 从队列中取出一个节点作为当前节点。

    b. 遍历当前节点的所有邻居节点:

    • 如果邻居节点未被访问过,则将其加入队列,并将其标记为已访问。
    • 同时记录该邻居节点的父节点,以便在最后重构路径时使用。
  4. 重构路径:从目标节点开始,通过父节点逐步回溯到起始节点,即可得到最短路径。

BFS算法通过逐层搜索,确保了首次到达目标节点的路径一定是最短路径。在实现过程中,可以利用一个字典或数组来记录节点的父节点,以方便路径的重构。

获取无权图的最短路径表

  • 代码

        /**
         * 获取无权图的最短路径表
         * 返回类型 为一个 int[vertices][3] 数组
         * int[i][0] 为是否能访问到(1.能,0.不能)
         * int[i][1] 距离
         * int[i][1] 到目的节点的前一个节点,-1代表没有前一个节点
         * @param origin
         * @return
         */    
        public int[][] getShortestPathTable(int origin){
            if(origin>=vertices){
                return null;
            }
            int[][] res = new int[vertices][3];
            Queue<Integer> queue = new LinkedList<>();
            res[origin-1][0] = 1;
            res[origin-1][1] = 0;
            res[origin-1][2] = -1;
            queue.offer(origin-1);
            while (!queue.isEmpty()){
                int newNode = queue.poll();
                for (int i = 0; i < vertices; i++) {
                    if (matrix[newNode][i]!=0){
                        if (res[i][0]!=1){
                            queue.offer(i);
                            res[i][0] = 1;
                            res[i][1] = res[newNode][1]+1;
                            res[i][2] = newNode;
                        }
                    }
                }
            }
            return res;
        }
    
    	public void shortestTableToString(int[][] shortTable){
            System.out.println("vertex\tvisit\tdist\tpath\t");
            for (int i = 0; i < vertices; i++) {
                String vertex = "v"+i;
                String visit = shortTable[i][0]==0?"false":"true";
                int dist = shortTable[i][1];
                String path = "v"+shortTable[i][2];
                System.out.printf("%s\t%s\t%d\t%s\t\n",vertex,visit,dist,path);
            }
        }
    
  • 测试代码

        public static void main(String[] args) {
            Graphs graphs = new Graphs();
            graphs.generate();
            int[][] shortTable=graphs.getShortestPathTable(3);
            graphs.shortestTableToString(shortTable);
        }
    
  • 测试结果
    在这里插入图片描述
    在这里插入图片描述

根据最短路径表获取最短路径

  • 代码

        public void shortestPathPrint(int[] path){
            int n = path.length;
            if (n==0){
                return;
            }
            System.out.print(path[0]);
            for (int i = 1; i <n ; i++) {
                System.out.print("-->"+path[i]);
            }
            System.out.println();
        }
    
    
        public int[] getShortestPathBytable(int origin,int end,int[][] shortTable){
            if (end>=vertices||end<0||origin>end||origin<0){
                return null;
            }
            if (shortTable[end][0]==0){
                return null;
            }
            List<Integer> list = new ArrayList<>();
            int newNode = end;
            while (shortTable[newNode][2] != -1){
                list.add(newNode);
                newNode = shortTable[newNode][2];
            }
            list.add(origin);
            int size = list.size();
            int[] res = new int[list.size()];
            for (int i = 0;i<size;i++){
                res[i] = list.get(size-i-1);
            }
            return res;
        }
    
  • 测试代码

        public static void main(String[] args) {
            Graphs graphs = new Graphs();
            graphs.generate();
            int[][] shortTable=graphs.getShortestPathTable(0);
            graphs.shortestTableToString(shortTable);
            int[] shortestPath = graphs.getShortestPathBytable(0,6,shortTable);
            graphs.shortestPathPrint(shortestPath);
        }
    
  • 结果

在这里插入图片描述

有权图的最短路径

有权图的最短路径问题可以使用一些经典算法来解决,最常用的算法是Dijkstra算法和Bellman-Ford算法。这两种算法都可以找到一个顶点到图中其他顶点的最短路径。

1. Dijkstra算法:

Dijkstra算法,由荷兰计算机科学家Edsger W. Dijkstra在1956年提出,是一种解决单源最短路径问题的算法。该算法是以图的顶点之间的边权重来计算最短路径的。

Dijkstra算法的基本思想是从起点开始,逐步确定到达各个顶点的最短路径。算法维护一个距离表,记录从起点到各个顶点的最短距离。初始化时,起点的最短距离为0,其他顶点的最短距离为无穷大。

算法的步骤如下:

  1. 初始化距离表和访问表。将起点标记为已访问,起点的最短距离为0,其他顶点的最短距离为无穷大。
  2. 选择距离表中未访问的顶点中最短距离的顶点作为当前顶点。
  3. 对当前顶点的邻居顶点,计算经由当前顶点到达邻居顶点的距离,并更新距离表中的最短距离。
  4. 更新完邻居顶点的最短距离后,将当前顶点标记为已访问。
  5. 重复步骤2至4,直到所有顶点都被访问过或者没有可达的顶点。

通过不断更新距离表,Dijkstra算法可以找到起点到其他顶点的最短路径。该算法的时间复杂度为O(V^2),其中V为顶点数。可以使用优先队列(如最小堆)来优化算法的时间复杂度至O((E+V)logV),其中E为边数。

  • 代码

        /**
         * 获取无权图的最短路径表
         * 返回类型 为一个 int[vertices][3] 数组
         * int[i][0] 为是否能访问到(1.能,0.不能)
         * int[i][1] 距离
         * int[i][2] 到目的节点的前一个节点,-1代表没有前一个节点
         * @param origin
         * @return
         */
        public int[][] getShortestPathTableDijkstra(int origin){
            if(origin>=vertices){
                return null;
            }
            int[][] res = new int[vertices][3];
            for(int i = 0;i<vertices;i++){
                res[i][1] = Integer.MAX_VALUE/2;
            }
            res[origin][0] = 1;
            res[origin][1] = 0;
            res[origin][2] = -1;
            int newNode = origin;
            while (newNode>=0){
                for (int i = 0; i < vertices; i++) {
                    if (matrix[newNode][i]!=0){
                        if (matrix[newNode][i]+res[newNode][1]<res[i][1]){
                            res[i][0] = 0;
                            res[i][1] = matrix[newNode][i]+res[newNode][1];
                            res[i][2] = newNode;
                        }
                    }
                }
                res[newNode][0] = 1;
                newNode = findMinDistance(res);
            }
            return res;
        }
    
        public int findMinDistance(int[][] res) {
            int minValue = Integer.MAX_VALUE;
            int minIndex = -1;
            for (int i = 0; i < vertices; i++) {
                if (res[i][0] == 0){
                    if (minIndex == -1||res[i][1]<minValue){
                        minValue = res[i][1];
                        minIndex = i;
                    }
                }
            }
            return minIndex;
        }
    
    
  • 测试结果
    在这里插入图片描述
    在这里插入图片描述

2. Bellman-Ford算法:

Bellman-Ford算法是解决单源最短路径问题的另一种算法,它可以处理含有负权边的图。Bellman-Ford算法的基本原理是通过迭代更新图中所有边的最短距离估计值,直到达到最优解。

Bellman-Ford算法的步骤如下:

  1. 初始化距离表,将起点的最短距离设为0,其他顶点的最短距离设为无穷大。
  2. 重复以下过程,执行V-1次:
    a. 对图中的每一条边,计算通过该边到达目标顶点的距离估计,并将其与当前最短距离进行比较。如果该距离估计小于当前最短距离,则更新最短距离。
  3. 检查图中的负权回路。对图中的每一条边,如果通过该边能够进一步减小最短距离,则说明存在负权回路,即图中存在无穷小的最短距离。

Bellman-Ford算法的时间复杂度为O(V*E),其中V是顶点数,E是边数。因为算法要进行V-1次迭代,每次迭代需要遍历所有的边。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值