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 小时前
[特殊字符] 第40课:二叉树最大深度
数据结构·算法·数据库架构·图论·bfs·二叉树最大深度
赫瑞3 小时前
数据结构中的排列组合 —— Java实现
java·开发语言·数据结构
大熊背3 小时前
ISP Pipeline中Lv实现方式探究之三--lv计算定点实现
数据结构·算法·自动曝光·lv·isppipeline
yuannl107 小时前
数据结构----队列的实现
数据结构
Mr_Xuhhh7 小时前
从ArrayList到LinkedList:理解链表,掌握Java集合的另一种选择
java·数据结构·链表
Ricardo-Yang9 小时前
SCNP语义分割边缘logits策略
数据结构·人工智能·python·深度学习·算法
soragui9 小时前
【Python】第 4 章:Python 数据结构实现
数据结构·windows·python
samroom10 小时前
【鸿蒙应用开发 Dev ECO Studio 5.0版本】从0到1!从无到有!最全!计算器------按钮动画、滑动退格、中缀表达式转后缀表达式、UI设计
数据结构·ui·华为·typescript·harmonyos·鸿蒙
算法鑫探10 小时前
10个数下标排序:最大值、最小值与平均值(下)
c语言·数据结构·算法·排序算法·新人首发
王老师青少年编程10 小时前
csp信奥赛c++之状压枚举
数据结构·c++·算法·csp·信奥赛·csp-s·状压枚举