代码随想录算法训练营第六十七天 | Floyd 算法精讲、A * 算法精讲(A star算法)、最短路算法总结、图论总结、复习

Floyd 算法 — 卡码网:97. 小明逛公园

题目链接:https://kamacoder.com/problempage.php?pid=1155
文档讲解:https://programmercarl.com/kamacoder/0097.%E5%B0%8F%E6%98%8E%E9%80%9B%E5%85%AC%E5%9B%AD.html

思路

Floyd 算法对边的权值正负没有要求,都可以处理。Floyd算法核心思想是动态规划

  1. 确定dp数组(dp table)以及下标的含义
    这里用 grid数组来存图,那就把dp数组命名为 grid。·grid[i][j][k] = m·,表示 节点i 到 节点j 以[1…k] 集合为中间节点的最短距离为m。
  2. 确定递推公式
    分两种情况:
    2.1. 节点i 到 节点j 的最短路径经过节点k
    2.2. 节点i 到 节点j 的最短路径不经过节点k
    对于第一种情况,grid[i][j][k] = grid[i][k][k - 1] + grid[k][j][k - 1]
    节点i 到 节点k 的最短距离 是不经过节点k,中间节点集合为[1…k-1],所以 表示为grid[i][k][k - 1];节点k 到 节点j 的最短距离 也是不经过节点k,中间节点集合为[1…k-1],所以表示为 grid[k][j][k - 1]
    第二种情况,grid[i][j][k] = grid[i][j][k - 1]
    如果节点i 到 节点j的最短距离 不经过节点k,那么 中间节点集合[1…k-1],表示为 grid[i][j][k - 1];因为我们是求最短路,对于这两种情况自然是取最小值。即:grid[i][j][k] = min(grid[i][k][k - 1] + grid[k][j][k - 1], grid[i][j][k - 1])
  3. dp数组如何初始化
    grid[i][j][k] = m,表示 节点i 到 节点j 以[1…k] 集合为中间节点的最短距离为m。刚开始初始化k 是不确定的。只能把k赋值为 0,本题节点0是无意义的,节点是从1 到 n。
  4. 确定遍历顺序
    从初始化来看,我们是把 k =0 的 i 和j 对应的数值都初始化了,这样才能去计算 k = 1 的时候 i 和 j 对应的数值。这就好比是一个三维坐标,i 和j 是平层,而k是垂直向上的。遍历的顺序是从底向上 一层一层去遍历。所以遍历k的for循环一定是在最外面,这样才能一层一层去遍历。至于遍历 i 和 j 的话,for 循环的先后顺序无所谓。

代码

import java.util.*;
class Main {
    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);
        int n = scanner.nextInt();
        int m = scanner.nextInt();
       int [][] edges = new int[n+1][n+1];
       for(int i = 0; i <= n; i++){
            for(int j = 0; j <= n; j++){
                edges[i][j] = 1000000;
            }
        }
        int s, e, v;
        for (int i = 0; i < m; i++) {
            s = scanner.nextInt();
            e = scanner.nextInt();
            v = scanner.nextInt();
            edges[s][e] = v;
            edges[e][s] = v; 
        }
        for(int k = 1; k <= n; k++){
            for(int i = 1; i <= n; i++){
                for(int j = 1; j <= n; j++){
                    edges[i][j] = Math.min(edges[i][j], edges[i][k]+edges[k][j]);
                }
            }
        }
        int row = scanner.nextInt();
        while(row-- > 0){
            int start = scanner.nextInt();
            int end = scanner.nextInt();
            if(edges[start][end] == 1000000){
                System.out.println(-1);
            }else{
                System.out.println(edges[start][end]);
            }
        }
    }
}

空间优化

从滚动数组的角度来看,我们定义一个grid[n + 1][ n + 1][2]这么大的数组就可以,因为k 只是依赖于 k-1的状态,并不需要记录k-2,k-3,k-4 等等这些状态。所以递归公式可以为:grid[i][j] = min(grid[i][j], grid[i][k] + grid[k][j]);

A * 算法 — 卡码网:126. 骑士的攻击

题目链接:https://kamacoder.com/problempage.php?pid=1203
文档讲解:https://programmercarl.com/kamacoder/0126.%E9%AA%91%E5%A3%AB%E7%9A%84%E6%94%BB%E5%87%BBastar.html

思路

Astar 是一种广搜的改良版。 有的说Astar是 dijkstra 的改良版。其实只是场景不同而已 我们在搜索最短路的时候, 如果是无权图(边的权值都是1) 那就用广搜,代码简洁,时间效率和 dijkstra 差不多 (具体要取决于图的稠密)。如果是有权图(边有不同的权值),优先考虑 dijkstra。而 Astar 关键在于 启发式函数, 也就是 影响 广搜或者 dijkstra 从 容器(队列)里取元素的优先顺序。以下用BFS版本的A * 来进行讲解。
BFS 是没有目的性的 一圈一圈去搜索, 而 A * 是有方向性的去搜索。A * 为什么可以有方向性的去搜索,它的如何知道方向呢?其关键在于启发式函数。指引搜索的方向的关键代码是:

int m=q.pop();
int n=q.pop();

从队列里取出什么元素,接下来就是从哪里开始搜索。所以启发式函数要影响的就是队列里元素的排序。
每个节点的权值为F,给出公式为:F = G + H

  • G:起点达到目前遍历节点的距离
  • F:目前遍历的节点到达终点的距离

起点达到目前遍历节点的距离 + 目前遍历的节点到达终点的距离 就是起点到达终点的距离。本题的图是无权网格状,在计算两点距离通常有如下三种计算方式:

  • 曼哈顿距离,计算方式: d = abs(x1-x2)+abs(y1-y2)
  • 欧氏距离(欧拉距离) ,计算方式:d = sqrt( (x1-x2)^2 + (y1-y2)^2)
  • 切比雪夫距离,计算方式:d = max(abs(x1 - x2), abs(y1 - y2))

本题,采用欧拉距离才能最大程度体现 点与点之间的距离。所以使用欧拉距离计算和广搜搜出来的最短路的节点数是一样的。 计算出来F之后,按照F的大小,来选去出队列的节点。可以使用优先级队列帮我们排好序,每次出队列,就是F最小的节点。

代码

import java.util.*;
class Main {
    static int[][] moves;
    static int dir[][] = {
            {-2, -1},
            {-2, 1},
            {-1, 2},
            {1, 2},
            {2, 1},
            {2, -1},
            {1, -2},
            {-1, -2},
    };
    static int c;
    static int d;
    static class Node {
        int x;
        int y;
        int g;
        int h;
        int f;
    }

    public static void main(String[] args) {
        moves = new int[1001][1001];
        PriorityQueue<Node> queue = new PriorityQueue<>(Comparator.comparingInt(aa -> aa.f));
        Scanner scanner = new Scanner(System.in);
        int n = scanner.nextInt();
        for (int i = 0; i < n; i++) {
            int a = scanner.nextInt();
            int b = scanner.nextInt();
            c = scanner.nextInt();
            d = scanner.nextInt();

            Node node = new Node();
            node.x = a;
            node.y = b;
            node.g = 0;
            node.h = distance(a, b);
            node.f = node.g + node.h;


            queue.offer(node);
            while (!queue.isEmpty()) {
                Node curNode = queue.poll();
                if (curNode.x == c && curNode.y == d) break;
                for (int ii = 0; ii < 8; ii++) {
                    int x = curNode.x + dir[ii][0];
                    int y = curNode.y + dir[ii][1];
                    if (x < 1 || x > 1000 || y < 1 || y > 1000)
                        continue;
                    if (moves[x][y] == 0) {
                        moves[x][y] = moves[curNode.x][curNode.y] + 1;
                        Node nextNode = new Node();
                        nextNode.x =x;
                        nextNode.y =y;
                         // 开始计算F
                        nextNode.g = curNode.g + 5; 
                        nextNode.h = distance(nextNode.x, nextNode.y);
                        nextNode.f = nextNode.g + nextNode.h;
                        queue.offer(nextNode);
                    }
                }
            }
            System.out.println(moves[c][d]);
            queue.clear();
            for (int[] move : moves) {
                Arrays.fill(move, 0); // 将第i行的所有元素初始化为-1
            }
        }
    }
    
    
    public static int distance(int x, int y) {
        return (x - c) * (x - c) + (y - d) * (y - d);
    }
}

最短路算法总结

文档讲解:https://programmercarl.com/kamacoder/%E6%9C%80%E7%9F%AD%E8%B7%AF%E9%97%AE%E9%A2%98%E6%80…

图论总结

文档讲解:https://programmercarl.com/kamacoder/%E5%9B%BE%E8%AE%BA%E6%80%BB%E7%BB%93%E7%AF%87.html

复习二叉树部分

235. 二叉搜索树的最近公共祖先
701.二叉搜索树中的插入操作
450.删除二叉搜索树中的节点

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值