day54 代码随想录算法训练营 图论专题8

1 今日打卡

拓扑排序 117. 软件构建

dijkstra朴素版 47. 参加科学大会(第六期模拟笔试)

2 拓扑排序

2.1 思路

构建图 + 统计入度:

用邻接表(umap)存储每个节点的后继节点(比如 S 的后继是 T)。

用数组(inDegree)统计每个节点的入度(指向该节点的边数,即该节点依赖的文件数)。

初始化队列:将所有入度为 0 的节点(无依赖的文件)加入队列。

处理队列:

取出队列中的节点(处理该文件),加入结果列表。

遍历该节点的所有后继节点(被该文件依赖的文件),将它们的入度减 1(因为依赖的文件已处理)。

如果某个后继节点入度变为 0(所有依赖都已处理),加入队列。

结果判断:

如果结果列表的长度等于节点数(N):无环,输出顺序。

否则:存在环(相互依赖),输出 - 1。

2.2 实现代码

java 复制代码
import java.util.*;
public class Main{
    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);
        int n = sc.nextInt(), m = sc.nextInt(); // n=文件数,m=依赖关系数
        // 1. 初始化邻接表(存储图):每个节点对应一个列表,存后继节点
        List<List<Integer>> umap = new ArrayList<>();
        for(int i = 0; i < n; i++) {
            umap.add(new ArrayList<>());
        }
        // 2. 初始化入度数组:统计每个节点的入度(依赖数)
        int[] inDegree = new int[n];
        for(int i = 0; i < m; i++) {
            int start = sc.nextInt(), end = sc.nextInt(); // S→T:T依赖S
            umap.get(start).add(end); // 邻接表添加边:start的后继是end
            inDegree[end]++; // end的入度+1(多了一个依赖)
        }
        // 3. 初始化队列:入度为0的节点(无依赖的文件)入队
        Queue<Integer> que = new LinkedList<>();
        List<Integer> res = new ArrayList<>(); // 存储拓扑排序结果
        for(int i = 0; i < n; i++) {
            if(inDegree[i] == 0) {
                que.add(i);
            }
        }
        // 4. 处理队列,逐步消除入度为0的节点
        while(!que.isEmpty()) {
            int cur = que.poll(); // 取出当前处理的节点
            res.add(cur); // 加入结果列表
            // 遍历当前节点的所有后继节点
            List<Integer> temp = umap.get(cur);
            for(int node : temp) {
                inDegree[node]--; // 后继节点的入度减1(依赖减少一个)
                if(inDegree[node] == 0) { // 所有依赖都处理完了
                    que.add(node); // 入队等待处理
                }
            }
        }
        // 5. 判断结果:是否所有节点都处理了(无环)
        if(res.size() == n) {
            // 输出顺序(空格分隔)
            for(int i = 0; i < n - 1; i++) {
                System.out.print(res.get(i) + " ");
            }
            System.out.println(res.get(n - 1));
        }else{
            System.out.println(-1); // 有环,输出-1
        }
    }
}

3 dijkstra朴素版

3.1 思路

初始化:

记录起点到所有节点的初始最短距离(起点到自己为 0,到其他节点为 "无穷大")。

记录节点是否已确定最短路径(避免重复处理)。

贪心选择:每次从未确定最短路径的节点中,选当前距离起点最近的节点(局部最优)。

松弛操作:通过这个选中的节点,更新其邻接节点的最短距离(尝试缩短路径)。

重复:直到所有节点的最短路径都被确定,或找到终点的最短路径。

3.2 实现代码

java 复制代码
import java.util.*;
public class Main{
    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);
        // 1. 输入基础参数:n=节点数,m=边数
        int n = sc.nextInt(), m = sc.nextInt();
        
        // 2. 初始化邻接矩阵(存储图的边和权重)
        // 邻接矩阵:adjacencyMatrix[i][j] 表示i到j的边的权重,无穷大表示无直接边
        int[][] adjacencyMatrix = new int[n+1][n+1];
        for(int i = 0; i <= n; i++) {
            // 初始化为无穷大(Integer.MAX_VALUE),表示初始时所有节点间无直接边
            Arrays.fill(adjacencyMatrix[i], Integer.MAX_VALUE);
        }
        
        // 3. 填充邻接矩阵(输入m条边)
        for(int i = 0; i < m; i++) {
            int from = sc.nextInt(), to = sc.nextInt(); // 边的起点、终点
            int weight = sc.nextInt(); // 边的权重(距离/成本)
            adjacencyMatrix[from][to] = weight; // 记录from→to的权重
        }
        
        // 4. 初始化最短距离数组和访问标记数组
        int[] minDist = new int[n + 1]; // minDist[i]:起点(1)到i的最短距离
        Arrays.fill(minDist, Integer.MAX_VALUE); // 初始都是无穷大(不可达)
        boolean[] visited = new boolean[n + 1]; // visited[i]:i的最短路径是否已确定
        
        // 5. 起点初始化:起点1到自己的距离为0
        int start = 1, end = n;
        minDist[start] = 0;
        
        // 6. 核心:迪杰斯特拉主循环(遍历n次,确定n个节点的最短路径)
        for(int i = 1; i <= n; i++) {
            // 6.1 贪心选择:找未访问的、距离起点最近的节点cur
            int minVal = Integer.MAX_VALUE; // 临时存储当前最小距离
            int cur = 1; // 临时存储选中的节点
            for(int j = 1; j <=n; j++) {
                // 条件:未访问 + 距离起点的距离更小
                if(!visited[j] && minDist[j] < minVal) {
                    minVal = minDist[j];
                    cur = j;
                }
            }
            
            // 6.2 标记cur的最短路径已确定(后续不再处理)
            visited[cur] = true;
            
            // 6.3 松弛操作:通过cur更新其邻接节点的最短距离
            for(int j = 1; j <= n; j++) {
                // 条件:
                // 1. j未访问(最短路径未确定)
                // 2. cur到j有直接边(不是无穷大)
                // 3. 起点→cur→j的路径 比 起点→j的当前最短路径 更短
                if(!visited[j] && adjacencyMatrix[cur][j] != Integer.MAX_VALUE 
                   && adjacencyMatrix[cur][j] + minDist[cur] < minDist[j]) {
                    // 更新最短距离(松弛)
                    minDist[j] = adjacencyMatrix[cur][j] + minDist[cur];
                }
            }
        }
        
        // 7. 输出结果:判断终点是否可达
        if(minDist[n] == Integer.MAX_VALUE) {
            System.out.println(-1); // 不可达,输出-1
        }else{
            System.out.println(minDist[end]); // 输出起点1到终点n的最短距离
        }
        sc.close();
    }
}
相关推荐
我能坚持多久2 小时前
【初阶数据结构10】——链式二叉树的功能实现
数据结构·算法
liuyao_xianhui2 小时前
优选算法_丢失的数字_位运算_C++
linux·数据结构·c++·算法·动态规划·哈希算法·散列表
啊哦呃咦唔鱼2 小时前
LeetCode hot100-239 滑动窗口最大值
数据结构·算法·leetcode
Book思议-2 小时前
【数据结构实战】 C 语言单链表通关:初始化 / 头插 / 尾插 / 增删改查全实现(附图解、可运行完整代码)
c语言·数据结构·算法
宵时待雨3 小时前
C++笔记归纳12:二叉搜索树
开发语言·数据结构·c++·笔记·算法
仰泳的熊猫3 小时前
题目2305:蓝桥杯2019年第十届省赛真题-等差数列
数据结构·c++·算法·蓝桥杯
jing-ya3 小时前
day 53 图论part5
java·数据结构·算法·图论
I_LPL3 小时前
hot100 图论专题
算法·图论·dfs·bfs·拓扑排序
ccLianLian3 小时前
图论·最短路径问题
图论