news 2026/4/18 0:40:16

【Java实现拓扑排序(从上游到下游) 功能】

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
【Java实现拓扑排序(从上游到下游) 功能】

文章目录

  • 问题记录
  • 一、问题分析
  • 二、实现思路
  • 三、Java 代码实现
  • 四、输出说明

问题记录

unitcd unitnm fcd ocd WCG10_2_6 南乐 -1 -1 WCG11_2_7 刘寨 -1 -1 WEA31_9_6 棉集 -1 -1 WEA31_9_8 大张庄 -1 -1 WEA36_9_10 黄口集闸下 -1 -1 WEA36_9_9 马桥 -1 -1 WEA37_9_11 李黑楼闸 -1 -1 WEA37_9_12 蒋庄 -1 -1 WEB37_9_1 南庄站 -1 -1 WCF11_1_18 占城 WCF11_1_r13 WCF11_1_12 WCF11_1_r13 石门水库(辉县)-1 WCF11_1_18 WCF11_1_22 南云门 -1 WCF11_1_12 WCF11_1_6 狮子营 -1 WCF11_1_12 WCF11_1_r2 马鞍石水库 -1 WCF11_1_12 WCF11_1_r37 宝泉水库 -1 WCF11_1_12 WCF11_1_r1 群英水库 -1 WCF11_1_4 WCF11_1_3 丰顺店 -1 WCF11_1_4 WCF11_1_4 修武 WCF11_1_r1;WCF11_1_3 WCF11_1_12 WCF11_1_17 花木 -1 WCF11_1_12 WCF11_1_12 合河共 WCF11_1_r2;WCF11_1_22;WCF11_1_4;WCF11_1_r37;WCF11_1_17;WCF11_1_18;WCF11_1_6 WCF11_1_21 WCF11_1_r14 弓上水库 -1 WCF11_1_r42 WCF11_1_r41 石门水库(安阳)-1 WCF11_1_r42 WCF11_1_r61 陈家院水库 -1 WCF11_1_r60 WCF11_1_r60 三郊口水库 WCF11_1_r61 WCF11_1_r42 WCF11_1_r42 盘石头水库 WCF11_1_r60;WCF11_1_r14;WCF11_1_r41 WCF11_1_32 WCF11_1_r54 八里营 -1 WCF11_1_r55 WCF11_1_r55 汲县 WCF11_1_r54 WCF11_1_r56 WCF11_1_r56 皇甫 WCF11_1_r55 WCF11_1_r57 WCF11_1_19 夺丰水库 -1 WCF11_1_r58 WCF11_1_21 黄土岗 WCF11_1_12 WCF11_1_r59 WCF11_1_38 正面水库 -1 WCF11_1_r39 WCF11_1_r39 狮豹头水库 WCF11_1_38 WCF11_1_40 WCF11_1_40 塔岗水库 WCF11_1_r39 WCF11_1_r59 WCF11_1_r59 良相坡 WCF11_1_21;WCF11_1_40 WCF11_1_r58 WCF11_1_32 新村 WCF11_1_r42 WCF11_1_r58 WCF11_1_r58 刘庄 WCF11_1_19;WCF11_1_r59;WCF11_1_32 WCF11_1_r57 WCF11_1_r57 淇门 WCF11_1_r56;WCF11_1_r58 WCF11_1_r44 WCF11_1_r44 牛寨 WCF11_1_r57 WCF11_1_r46 WCF11_1_r43 牛庄 -1 WCF11_1_r45 WCF11_1_r45 道口()WCF11_1_r43 WCF11_1_r46 WCF11_1_r46 浚县 WCF11_1_r44;WCF11_1_r45 WCF11_1_r49 WCF11_1_r47 白寺坡 -1 WCF11_1_r48 WCF11_1_r48 屯子 WCF11_1_r47 WCF11_1_r49 WCF11_1_r49 五陵 WCF11_1_r48;WCF11_1_r46 WCF11_1_r50 WCF11_1_r29 汤河水库 -1 WCF11_1_r50 WCF11_1_r30 琵琶寺水库 -1 WCF11_1_r50 WCF11_1_34 梨园 -1 WCF11_1_r50 WCF11_1_r50 西元村 WCF11_1_r30;WCF11_1_r29;WCF11_1_34;WCF11_1_r49 WCF11_1_r52 WCF11_1_r24 双泉水库 -1 WCF11_1_r27 WCF11_1_15 横水 -1 WCF11_1_r9 WCF11_1_r9 小南海水库 WCF11_1_15 WCF11_1_r23 WCF11_1_r23 彰武水库 WCF11_1_r9 WCF11_1_r27 WCF11_1_r27 安阳 WCF11_1_r23;WCF11_1_r24 WCF11_1_r52 WCF11_1_r51 内黄 -1 WCF11_1_r53 WCF11_1_r52 楚旺 WCF11_1_r27;WCF11_1_r50 WCF11_1_r53 WCF11_1_r53 元村集 WCF11_1_r52;WCF11_1_r51 -1

以上示例数据,按照从上游到下游的顺序,一般会将上下游都是-1(即独立的,没有上下游的数据)的排在最前面。
请将以上数据进行按照从上游到下游的排序,注意上游fcd是按照分号进行分割的,可能存在多个上游,并生成order_num字段,编码从1开始递增。java代码实现,类名为TopologySorter。
数据存储在表中:
String inputFilePath = “C:\Users\Administrator\Desktop\拓扑排序输入.xlsx”;
String outputFilePath = “C:\Users\Administrator\Desktop\拓扑排序输入_排序后.xlsx”;


一、问题分析

为了实现上面描述的 拓扑排序(从上游到下游) 功能,我们需要对 Excel 中的河道数据进行 有向无环图(DAG)拓扑排序。规则如下:
1.每一行代表一个节点(unitcd);
2.fcd 字段表示该节点的上游节点列表(多个用分号分隔,-1 表示无上游);
3.ocd 字段表示该节点的下游节点(单个,-1 表示无下游);
4.我们的目标是:从没有上游的节点开始(即 fcd = -1),逐层向下排序;
5.生成 order_num 字段,从 1 开始递增。

二、实现思路

1.读取 Excel 数据 到内存对象;
2.构建图结构:
每个节点记录其所有上游节点(in-degree);
构建 节点 → 下游节点列表 的邻接表(用于 BFS 遍历);
3.使用 Kahn 算法进行拓扑排序:
入度为 0 的节点(即 fcd = “-1” 或所有上游都不存在/未定义)入队;
逐层处理,减少下游节点的入度;
4.处理孤立节点(上下游均为 -1)放在最前面;
5.写回 Excel,新增 order_num 列。

三、Java 代码实现

(类名:TopologySorter)
依赖:Apache POI(同上)

importorg.apache.poi.ss.usermodel.*;importorg.apache.poi.xssf.usermodel.XSSFWorkbook;importjava.io.FileInputStream;importjava.io.FileOutputStream;importjava.io.IOException;importjava.util.*;publicclassTopologySorter{publicstaticvoidmain(String[]args){StringinputFilePath="C:\\Users\\Administrator\\Desktop\\拓扑排序输入.xlsx";StringoutputFilePath="C:\\Users\\Administrator\\Desktop\\拓扑排序输入_排序后.xlsx";try{sortAndWrite(inputFilePath,outputFilePath);}catch(IOExceptione){e.printStackTrace();}}privatestaticvoidsortAndWrite(StringinputPath,StringoutputPath)throwsIOException{// 1. 读取数据List<Node>nodes=readExcel(inputPath);Map<String,Node>nodeMap=newHashMap<>();for(Nodenode:nodes){nodeMap.put(node.unitcd,node);}// 2. 构建图:计算每个节点的入度(in-degree)Map<String,Integer>inDegree=newHashMap<>();Map<String,List<String>>adjList=newHashMap<>();// node -> downstream list// 初始化for(Nodenode:nodes){inDegree.put(node.unitcd,0);adjList.put(node.unitcd,newArrayList<>());}// 遍历每个节点,解析 fcd(上游),建立反向边:上游 → 当前节点(即当前节点的入度来源)for(Nodenode:nodes){if("-1".equals(node.fcd)){// 无上游,入度为0continue;}String[]upstreams=node.fcd.split(";");for(Stringup:upstreams){if(nodeMap.containsKey(up)){// up 是有效上游节点adjList.computeIfAbsent(up,k->newArrayList<>()).add(node.unitcd);inDegree.merge(node.unitcd,1,Integer::sum);}// 如果 upstream 不存在于本表中,忽略(视为外部源)}}// 3. Kahn 拓扑排序Queue<String>queue=newLinkedList<>();// 所有入度为0的节点入队(包括 fcd=-1 且无其他上游的)for(Stringunitcd:inDegree.keySet()){if(inDegree.get(unitcd)==0){queue.offer(unitcd);}}List<String>sortedOrder=newArrayList<>();while(!queue.isEmpty()){Stringcurrent=queue.poll();sortedOrder.add(current);// 遍历当前节点的所有下游for(Stringdownstream:adjList.getOrDefault(current,Collections.emptyList())){inDegree.put(downstream,inDegree.get(downstream)-1);if(inDegree.get(downstream)==0){queue.offer(downstream);}}}// 4. 处理可能的环(本题假设无环,可忽略)if(sortedOrder.size()!=nodes.size()){System.err.println("警告:存在环或孤立节点未完全处理,排序节点数: "+sortedOrder.size()+", 总数: "+nodes.size());}// 5. 为每个节点分配 order_numMap<String,Integer>orderMap=newHashMap<>();for(inti=0;i<sortedOrder.size();i++){orderMap.put(sortedOrder.get(i),i+1);}// 对于不在 sortedOrder 中的节点(理论上不应有),补到最后for(Nodenode:nodes){if(!orderMap.containsKey(node.unitcd)){orderMap.put(node.unitcd,orderMap.size()+1);}}// 6. 写回 ExcelwriteExcelWithOrder(inputPath,outputPath,orderMap);}privatestaticList<Node>readExcel(Stringpath)throwsIOException{FileInputStreamfis=newFileInputStream(path);Workbookworkbook=newXSSFWorkbook(fis);Sheetsheet=workbook.getSheetAt(0);List<Node>nodes=newArrayList<>();booleanfirstRow=true;for(Rowrow:sheet){if(firstRow){firstRow=false;continue;// 跳过标题行(如果存在)}CellunitcdCell=row.getCell(0);CellunitnmCell=row.getCell(1);CellfcdCell=row.getCell(2);CellocdCell=row.getCell(3);Stringunitcd=getCellValueAsString(unitcdCell);Stringunitnm=getCellValueAsString(unitnmCell);Stringfcd=getCellValueAsString(fcdCell);Stringocd=getCellValueAsString(ocdCell);if(unitcd==null||unitcd.trim().isEmpty())continue;nodes.add(newNode(unitcd,unitnm,fcd,ocd));}workbook.close();fis.close();returnnodes;}privatestaticvoidwriteExcelWithOrder(StringinputPath,StringoutputPath,Map<String,Integer>orderMap)throwsIOException{FileInputStreamfis=newFileInputStream(inputPath);Workbookworkbook=newXSSFWorkbook(fis);Sheetsheet=workbook.getSheetAt(0);// 在第一行添加 order_num 列头(假设原表有标题行)RowheaderRow=sheet.getRow(0);if(headerRow==null){headerRow=sheet.createRow(0);}intorderColIndex=4;// 原有4列(0~3),新列在第5列(索引4)headerRow.createCell(orderColIndex).setCellValue("order_num");// 从第二行开始写 order_numfor(inti=1;i<=sheet.getLastRowNum();i++){Rowrow=sheet.getRow(i);if(row==null)continue;CellunitcdCell=row.getCell(0);Stringunitcd=getCellValueAsString(unitcdCell);if(unitcd==null)continue;Integerorder=orderMap.get(unitcd);if(order==null)order=999999;// fallbackrow.createCell(orderColIndex).setCellValue(order);}// 写出文件FileOutputStreamfos=newFileOutputStream(outputPath);workbook.write(fos);fos.close();workbook.close();fis.close();}privatestaticStringgetCellValueAsString(Cellcell){if(cell==null)returnnull;switch(cell.getCellType()){caseSTRING:returncell.getStringCellValue().trim();caseNUMERIC:// 尝试判断是否为整数(避免 1.0 变成 "1.0")doubleval=cell.getNumericCellValue();if(val==Math.floor(val)&&!Double.isInfinite(val)){returnString.valueOf((long)val);}else{returnString.valueOf(val);}caseBLANK:return"";default:returncell.toString();}}// 内部数据类staticclassNode{Stringunitcd;Stringunitnm;Stringfcd;Stringocd;Node(Stringunitcd,Stringunitnm,Stringfcd,Stringocd){this.unitcd=unitcd;this.unitnm=unitnm;this.fcd=fcd;this.ocd=ocd;}}}

四、输出说明

输入 Excel 必须包含列:unitcd, unitnm, fcd, ocd(顺序一致即可);
程序会自动跳过第一行(假设是标题);
输出文件在原表基础上新增一列 order_num,值从 1 开始;
上游无依赖的节点(如 fcd = -1)会排在最前面;
支持多上游(fcd 含分号);
自动处理图结构,确保拓扑顺序正确。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/4/17 21:14:23

如何用 4 种可靠的方法更换 iPhone

Apple 每年都会发布新版本的 iPhone。升级到新 iPhone 是一种令人兴奋的体验&#xff0c;但转移所有宝贵数据的想法有时会让人感到畏惧。幸运的是&#xff0c;我们准备了 4 种有效的更换 iPhone 的方法&#xff0c;让你可以毫不费力地更换到你的新 iPhone。此外&#xff0c;您还…

作者头像 李华
网站建设 2026/4/18 8:44:20

雅思套招小Tips(未完待续)

以下是雅思口语中常见场景的应对思路、精准用词及例句&#xff0c;涵盖高频话题&#xff0c;注重场景全面性和表达准确性&#xff1a;一、家乡&#xff08;Hometown&#xff09;常见问题&#xff1a;Where is your hometown?What’s special about your hometown?Do you like…

作者头像 李华
网站建设 2026/4/18 5:59:36

区块链 Web3 项目开发

区块链 Web3 项目的开发在 2026 年已经进入了“生产力时代”。与早期追求炒作不同&#xff0c;现在的开发更强调实用性&#xff08;Utility-First&#xff09;、合规性以及与 AI 智能体&#xff08;AI Agent&#xff09;的深度融合。以下是区块链 Web3 项目开发的标准流程与核心…

作者头像 李华
网站建设 2026/4/18 4:45:56

Flutter 逆向想学却无从下手?

2026 我和 Lane 联合出品硬核课程来了&#xff01;Flutter 运行原理与Blutter项目解析各种python脚本以及实现原理分享内存 Dump 大量实战 Hook丰富案例 flutter-reverse项目实战不止 Flutter&#xff0c;后续还会分享更多案例demo全是真刀真枪的干货&#xff01;私信主页 V …

作者头像 李华