当前位置: 首页 > news >正文

dfs代码问题根源分析

目录

1. 核心超时原因

2. 正确建图思路

优化后完整代码(DFS 版,AC 不超时)

关键优化点对比

旧代码缺陷

新版优化点

补充:BFS 迭代版(防止递归深度过大栈溢出)

原理简单说明


1. 核心超时原因

  1. 双重循环 O (n²)dfs 里写了for(int i = 0; i < n; i++),每次遍历全部 n 个节点,n 大时直接爆炸。
  2. List.contains () 是 O (k) 线性查找graph.get(pos).contains(i)每次遍历邻接表,双重嵌套复杂度极高。
  3. 图只存单向边,没有标记原道路方向原数组connections=[a,b]代表原生道路 a→b,需要反转;而 b→a 是虚拟反向边不用反转。你现在单纯存单向列表,判断是否要翻转的逻辑低效且容易出错。

2. 正确建图思路

每条道路[u, v]

  • u → v:真实道路,走到这里需要翻转,标记权重1
  • v → u:虚拟反向通路,不用翻转,标记权重0邻接表存(邻接点, 是否需要翻转),一次遍历就能拿到所有邻居,不用循环全部节点。

优化后完整代码(DFS 版,AC 不超时)

import java.util.ArrayList; import java.util.List; class Solution { int res = 0; public int minReorder(int n, int[][] connections) { // 邻接表:List<int[]>,每个数组 {neighbor, cost} // cost=1:原道路是 当前节点→邻居,需要翻转 // cost=0:虚拟反向边,不需要翻转 List<List<int[]>> graph = new ArrayList<>(); for (int i = 0; i < n; i++) { graph.add(new ArrayList<>()); } // 构建双向带权图 for (int[] edge : connections) { int u = edge[0]; int v = edge[1]; graph.get(u).add(new int[]{v, 1}); // u->v 真实路,代价+1 graph.get(v).add(new int[]{u, 0}); // v->u 虚拟路,代价0 } boolean[] visited = new boolean[n]; dfs(graph, 0, visited); return res; } private void dfs(List<List<int[]>> graph, int cur, boolean[] visited) { visited[cur] = true; // 只遍历当前节点的所有邻接点,O(邻接点数量),不是O(n) for (int[] neighborInfo : graph.get(cur)) { int next = neighborInfo[0]; int cost = neighborInfo[1]; if (!visited[next]) { res += cost; dfs(graph, next, visited); } } } }

关键优化点对比

旧代码缺陷

  1. dfs 循环0~n-1所有节点,n=5e4 直接超时
  2. contains()线性查找,双重循环叠加复杂度爆炸
  3. 无法快速区分 “原正向路 / 虚拟反向路”,逻辑绕且慢

新版优化点

  1. 邻接表存储带权边一次遍历当前节点所有邻居,时间复杂度 O (n),总节点 + 边只遍历一次
  2. 去掉List.contains(),完全消除线性查找开销
  3. 建图时直接标记是否需要翻转,遍历邻居时直接累加代价,逻辑极简
  4. 整体时间复杂度 O (n),空间 O (n),大数据量无压力

补充:BFS 迭代版(防止递归深度过大栈溢出)

当 n 极大时,递归 DFS 会栈溢出,可用 BFS:

import java.util.ArrayList; import java.util.LinkedList; import java.util.List; import java.util.Queue; class Solution { public int minReorder(int n, int[][] connections) { List<List<int[]>> graph = new ArrayList<>(); for (int i = 0; i < n; i++) graph.add(new ArrayList<>()); for (int[] e : connections) { int u = e[0], v = e[1]; graph.get(u).add(new int[]{v, 1}); graph.get(v).add(new int[]{u, 0}); } boolean[] vis = new boolean[n]; Queue<Integer> q = new LinkedList<>(); q.offer(0); vis[0] = true; int ans = 0; while (!q.isEmpty()) { int cur = q.poll(); for (int[] info : graph.get(cur)) { int next = info[0], cost = info[1]; if (!vis[next]) { vis[next] = true; ans += cost; q.offer(next); } } } return ans; } }

原理简单说明

题目要求所有城市能通向 0,从 0 向外遍历:

  • 如果遍历方向和原始道路方向一致(u→v),说明这条路远离 0,必须翻转,计数 + 1
  • 如果是反向虚拟边(v→u),道路本身朝向 0,无需翻转,不计入结果 遍历所有节点累加翻转次数即为答案。
http://www.cnnetsun.cn/news/2995538.html

相关文章:

  • TikTok国际版下载避坑指南:2026年最新完整教程
  • 独立产品从0到1:技术人的产品打磨方法论
  • 【共创季稿事节】动图魔方技术拆解 03:HarmonyOS 6.1 本地优先 GIF 工具:素材选择、文件 URI、相册保存与系统分享
  • 狼享Lite版(LAN Share Lite) 教程
  • 性价比高的中高端整装家居公司
  • Prompt
  • 终极指南:Super IO插件深度解析与Blender高效工作流优化
  • XPath定位革命:告别冗长代码,3分钟掌握智能元素定位神器
  • 手语AI翻译革命:如何用3行代码构建端到端手语识别系统
  • 景里雨竹|200-300 人 小众活动场地
  • 085、STM32项目分享开源:智能饮水机控制系统
  • 终极指南:如何用现代C++技术重制经典武侠游戏《金庸群侠传》
  • 3分钟掌握KISS Translator:让你的跨语言阅读效率提升300%
  • Dify 1.14 的 advanced-chat 工作流流式
  • 八角基因组--文献精读249
  • 电池内阻测试仪技术全解析:从 AC 毫欧法到四线法 Kelvin 连接
  • YimMenu终极教程:GTA5最强防护与功能增强菜单配置指南
  • 2026 企业智能体开发平台全景评测:八大主流平台横向对比
  • 微信聊天记录本地化备份:完全掌控你的数据隐私与存储空间
  • web作业七
  • 深度解构PDFPatcher:.NET生态下的PDF处理技术实现内幕
  • 如何快速搭建Arduino ESP32开发环境:新手完整指南
  • NVIC_SYSTEMRESET失败卡死
  • 6.24线上DevCon预约:OpenVINO™开源AI朋友圈,等你来加入
  • RTranslator离线翻译模型快速部署终极指南:告别漫长下载,5分钟完成安装
  • HarmonyOS ArkUI 自定义跑道布局:CustomMultiChildLayout 模式深度实践
  • Emscripten如何重塑Web技术栈:从原生代码到WebAssembly的战略架构迁移
  • 如何用Globe.GL打造惊艳的3D地球数据可视化:从零到一的实战指南
  • 36氪新浪潮大会:值得买科技朱越分享AI时代消费决策链路变化与品牌应对策略
  • 易元智创APP:AI智能画面去杂物,海南易元现实科技有限公司一键净化实拍场景