首页 > 编程语言 >代码随想录算法训练营第六十五天 | dijkstra(堆优化版)精讲、Bellman_ford 算法精讲、复习

代码随想录算法训练营第六十五天 | dijkstra(堆优化版)精讲、Bellman_ford 算法精讲、复习

时间:2024-07-15 20:55:59浏览次数:19  
标签:int 精讲 源点 算法 minDist grid new 节点 第六十五

dijkstra(堆优化版)精讲 — 卡码网:47. 参加科学大会

题目链接:https://kamacoder.com/problempage.php?pid=1047
文档讲解:https://programmercarl.com/kamacoder/0047.%E5%8F%82%E4%BC%9Adijkstra%E5%A0%86.html

思路

当节点数多,边数少(稀疏图)时,可以考虑从边的角度出发,用堆来优化dijkstra算法。dijkstra算法三部曲为:

  • 第一步,选源点到哪个节点近且该节点未被访问过
  • 第二步,该最近节点被标记访问过
  • 第三步,更新非访问节点到源点的距离(即更新minDist数组)

那么当从边的角度出发, 在处理三部曲里的第一步(选源点到哪个节点近且该节点未被访问过)的时候 ,我们可以不用去遍历所有节点了。而且直接把边(带权值)加入到小顶堆(利用堆来自动排序),那么每次我们从堆顶里取出边自然就是距离源点最近的节点所在的边。这样我们就不需要两层for循环来寻找最近的节点了。

代码

import java.util.*;

class Edge {
    int to;  // 邻接顶点
    int val; // 边的权重

    Edge(int t, int w) {
        this.to = t;
        this.val = w;
    }
}

public class Main {
    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);
        int n = sc.nextInt();
        int m = sc.nextInt();

        List<List<Edge>> grid = new ArrayList<>();
        for (int i = 0; i <= n; i++) {
            grid.add(new ArrayList<>());
        }

        for (int i = 0; i < m; i++) {
            int p1 = sc.nextInt();
            int p2 = sc.nextInt();
            int val = sc.nextInt();
            grid.get(p1).add(new Edge(p2, val));
        }

        int start = 1;  // 起点
        int end = n;    // 终点

        // 存储从源点到每个节点的最短距离
        int[] minDist = new int[n + 1];
        Arrays.fill(minDist, Integer.MAX_VALUE);

        // 记录顶点是否被访问过
        boolean[] visited = new boolean[n + 1];

        // 优先队列中存放 int[]{节点,源点到该节点的距离}
        PriorityQueue<int[]> pq = new PriorityQueue<>(new Comparator<int[]>() {
            @Override
            public int compare(int[] o1, int[] o2) { // o1[0]中放节点,o1[1]中放圆源点到该节点的距离
                return o1[1] - o2[1]; // 小顶堆
            }
        });

        // 初始化队列,源点到源点的距离为0,所以初始为0
        pq.offer(new int[]{start, 0});

        minDist[start] = 0;  // 起始点到自身的距离为0

        while (!pq.isEmpty()) {
            // 1. 第一步,选源点到哪个节点近且该节点未被访问过 (通过优先级队列来实现)
            int[] cur = pq.poll();

            if (visited[cur[0]]) continue;

            // 2. 第二步,该最近节点被标记访问过
            visited[cur[0]] = true;

            // 3. 第三步,更新非访问节点到源点的距离(即更新minDist数组)
            for (Edge edge : grid.get(cur[0])) { // 遍历 cur 指向的节点,cur 指向的节点为 edge
                if (!visited[edge.to] && minDist[cur[0]] + edge.val < minDist[edge.to]) { // 更新 minDist
                    minDist[edge.to] = minDist[cur[0]] + edge.val;
                    pq.offer(new int[]{edge.to, minDist[edge.to]});
                }
            }
        }

        if (minDist[end] == Integer.MAX_VALUE) {
            System.out.println(-1); // 不能到达终点
        } else {
            System.out.println(minDist[end]); // 到达终点最短路径
        }
    }
}

Bellman_ford 算法精讲 — 卡码网:94. 城市间货物运输 I

题目链接:https://kamacoder.com/problempage.php?pid=1152
文档讲解:https://programmercarl.com/kamacoder/0094.%E5%9F%8E%E5%B8%82%E9%97%B4%E8%B4%A7%E7%89%A9%E8…

思路

本题中边的权值有负数,是带负权值的单源最短路问题,所以用到Bellman_ford 算法。Bellman_ford算法的核心思想是对所有边进行松弛n-1次操作(n为节点数量),从而求得目标最短路。

什么是松弛?
  • minDist[B]表示到达B节点最小权值。如果通过 A 到 B 这条边可以获得更短的到达B节点的路径,即如果minDist[B] > minDist[A] + value ,那么我们就更新minDist[B] = minDist[A] + value,这个过程就叫做 “松弛” 。也可以这么写:minDist[B] = min(minDist[A] + value, minDist[B])

Bellman_ford算法也是采用了动态规划的思想,即:将一个问题分解成多个决策阶段,通过状态之间的递归关系最后计算出全局最优解。

为什么要对所有边松弛n-1次?

对所有边松弛一次 能得到 与起点 一条边相连的节点最短距离。
对所有边松弛两次 可以得到与起点 两条边相连的节点的最短距离。
对所有边松弛三次 可以得到与起点 三条边相连的节点的最短距离。
那么需要对所有边松弛n-1次才能得到 起点(节点1) 到终点(节点n)的最短距离。

代码

import java.util.*;
class Main{
    public static void main (String[] args) {
        Scanner in = new Scanner(System.in);
        int n = in.nextInt(), m = in.nextInt();
        int[][] grid = new int[m][3];
        int[] minDist = new int[n + 1];
        Arrays.fill(minDist, Integer.MAX_VALUE);
        for (int i = 0; i < m; i++) {
            grid[i][0] = in.nextInt();
            grid[i][1] = in.nextInt();
            grid[i][2] = in.nextInt();
        }
        int start = 1, end = n;
        minDist[start] = 0;
        for (int i = 0; i < n; i++) {
            for (int j = 0; j < m; j++) {
                int from = grid[j][0], to = grid[j][1], price = grid[j][2];
                if (minDist[from] != Integer.MAX_VALUE && minDist[to] > minDist[from] + price) {
                    minDist[to] = minDist[from] + price;
                }
            }
        }
        System.out.println(minDist[end] == Integer.MAX_VALUE ? "unconnected" : minDist[end]);
    }
}

复习二叉树部分

654.最大二叉树
617.合并二叉树
700.二叉搜索树中的搜索
98.验证二叉搜索树

标签:int,精讲,源点,算法,minDist,grid,new,节点,第六十五
From: https://blog.csdn.net/Danny_8/article/details/140361656

相关文章

  • 【Unity】凸包算法对比及实现
    背景在做闵可夫斯基差的可视化的时候,获得了很多个点,想要知道其是否包含原点,需要连接一个包裹这些点的最小凸多边形。因此就单独研究了这个部分,实现了功能并进行分析对比。凸包算法可以在多个散落的点中找到最小能包裹它的外壳,像套上一个橡皮筋一样。这里主要采用Graham算法进行代......
  • Vue--DIFF 算法
    一、什么是DIFF算法?DIFF算法用于比较两棵虚拟DOM树的差异,从而生成最小化的DOM更新操作。这个过程通常分为以下几个步骤:树形结构的对比:逐层对比新旧虚拟DOM树,找出差异节点。最小化更新:对实际DOM进行最小量的修改,以反映虚拟DOM的变化。二、Vue的DIFF算法原理......
  • 单链表算法 - 链表的中间节点
    .-力扣(LeetCode).-备战技术面试?力扣提供海量技术面试资源,帮助你高效提升编程技能,轻松拿下世界IT名企DreamOffer。https://leetcode.cn/problems/middle-of-the-linked-list/description/思路1: 思路2:代码:/***Definitionforsingly-linkedlist.*struct......
  • 多源谱修复学习算法(Multi-source Spectral Repair Learning Algorithm, MSRL)
    多源谱修复学习算法(Multi-sourceSpectralRepairLearningAlgorithm,MSRL)是一种针对非完备多源数据的处理方法,旨在解决因数据缺失而导致的多源数据学习问题。非完备多源数据是指在数据采集过程中,由于各种原因(如数据源多样性带来的质量差异或数据获取能力限制),导致某些样......
  • 多源谱嵌入融合学习算法(Multi-source Spectral Embedding Fusion Learning Algorithm,
    多源谱嵌入融合学习算法(Multi-sourceSpectralEmbeddingFusionLearningAlgorithm,简称MSEF)是一种专门设计用于处理多源数据的高级学习方法,其目标是在不同数据源之间建立一致的表示,从而提高聚类性能和数据理解的全面性。这种算法的核心在于利用全局和局部谱嵌入的融合,以......
  • 启发式算法(Heuristic Algorithm)
    启发式算法(HeuristicAlgorithm)是一类用于解决复杂问题的算法,通过利用问题的某些特征和经验规则,在可接受的时间范围内找到较好的近似解。启发式算法不保证找到最优解,但通常可以在合理的计算时间内获得可行且质量较高的解。启发式算法的思想启发式算法的核心思想是通过利用......
  • 分布式中唯一ID生成算法
    前言分布式系统中,难免会需要生成唯一ID作为标识符的需求。数据库主键,订单系统,日志系统,消息队列,会话管理,当并发量巨大且需要唯一标识信息的ID时,唯一ID生成算法就显得非常重要。UUIDUUID(UniversallyUniqueIdentifier,通用唯一标识符)是一种标准化的唯一标识符生成算法,它能够在全......
  • 「代码随想录算法训练营」第十一天 | 二叉树 part1
    二叉树的基本知识链接:https://programmercarl.com/二叉树理论基础.html要点:深度优先遍历前序遍历(递归法,迭代法)中序遍历(递归法,迭代法)后序遍历(递归法,迭代法)广度优先遍历层次遍历(迭代法)由于栈就是递归的一种实现结构,因此前中后序遍历的逻辑可以借助栈使用递归的方式......
  • 数据结构的基础(集合框架算法,复杂度和泛型)
    一.什么是集合框架        Java集合框架JavaCollectionFramework,又被称为容器container,是定义在java.util包下的一组接口interfaces和其实现类classes。        其主要表现为将多个元素element置于一个单元中,用于对这些元素进行......
  • 代码随想录算法训练营第22天 |二叉树part07:235. 二叉搜索树的最近公共祖先、701.二叉
    代码随想录算法训练营第22天|二叉树part07:235.二叉搜索树的最近公共祖先、701.二叉搜索树中的插入操作、450.删除二叉搜索树中的节点235.二叉搜索树的最近公共祖先https://leetcode.cn/problems/lowest-common-ancestor-of-a-binary-search-tree/description/代码随想录:htt......