Cycle Sort循环排序算法的Java实现

233 篇文章 ¥59.90 ¥99.00
本文介绍了Cycle Sort循环排序算法的原理和Java实现。该算法通过遍历数组,找到每个元素正确位置并交换,直至所有元素排序完成。提供的Java代码示例展示了如何在实际中应用Cycle Sort。

Cycle Sort循环排序算法的Java实现

循环排序算法是一种比较简单但高效的排序算法。它主要用于对数组进行排序,并且具有原地排序的特点,不需要额外的空间。在本文中,我将为您介绍Cycle Sort算法的实现,并提供相应的Java代码示例。

Cycle Sort算法的原理是通过不断地将元素放到它应该出现的位置来进行排序。具体而言,它通过遍历数组,对每个元素找到其正确的位置,并将其交换到正确位置上。这个过程会不断重复,直到所有元素都被放置到正确的位置上。

下面是Cycle Sort算法的Java实现代码:

public class CycleSort {
   
   
    public static void cycleSort(int[
<think>拓扑排序是针对有向无环图(DAG)的一种线性排序算法,使得对于任何有向边(u,v),节点u在排序中出现在节点v之前。以下是使用Java实现拓扑排序的两种方法:Kahn算法(基于入度)和DFS(深度优先搜索)算法。###1.Kahn算法(基于入度)Kahn算法通过不断移除入度为0的节点来完成拓扑排序。```javaimportjava.util.*;publicclassTopologicalSortKahn{publicstaticList<Integer>topologicalSort(intnumVertices,List<List<Integer>>graph){//计算每个节点的入度int[]inDegree=newint[numVertices];for(inti=0;i<numVertices;i++){for(intneighbor:graph.get(i)){inDegree[neighbor]++;}}//将入度为0的节点加入队列Queue<Integer>queue=newLinkedList<>();for(inti=0;i<numVertices;i++){if(inDegree[i]==0){queue.offer(i);}}List<Integer>result=newArrayList<>();intvisitedCount=0;while(!queue.isEmpty()){intnode=queue.poll();result.add(node);visitedCount++;//减少邻居节点的入度for(intneighbor:graph.get(node)){inDegree[neighbor]--;if(inDegree[neighbor]==0){queue.offer(neighbor);}}}//如果图中存在环,则无法完成拓扑排序if(visitedCount!=numVertices){thrownewIllegalStateException("Graphhasacycle");}returnresult;}publicstaticvoidmain(String[]args){//示例:构建有向图(邻接表表示)intnumVertices=6;List<List<Integer>>graph=newArrayList<>();for(inti=0;i<numVertices;i++){graph.add(newArrayList<>());}//添加边graph.get(5).add(2);graph.get(5).add(0);graph.get(4).add(0);graph.get(4).add(1);graph.get(2).add(3);graph.get(3).add(1);//拓扑排序List<Integer>sortedOrder=topologicalSort(numVertices,graph);System.out.println("拓扑排序结果(Kahn算法):"+sortedOrder);}}```**解释**:1.**初始化入度数组**:遍历图的邻接表,计算每个节点的入度。2.**初始化队列**:将所有入度为0的节点加入队列。3.**处理队列**:-从队列中取出节点,加入结果列表。-遍历该节点的所有邻居,将邻居的入度减1。-若邻居的入度变为0,则加入队列。4.**检查环**:如果结果列表中的节点数不等于图中节点总数,说明图中存在环,无法进行拓扑排序。###2.DFS算法(深度优先搜索)使用DFS进行拓扑排序,需要在DFS完成遍历节点后,将节点插入结果列表的头部(逆序)。```javaimportjava.util.*;publicclassTopologicalSortDFS{publicstaticList<Integer>topologicalSort(intnumVertices,List<List<Integer>>graph){boolean[]visited=newboolean[numVertices];Stack<Integer>stack=newStack<>();//对每个未访问节点调用DFSfor(inti=0;i<numVertices;i++){if(!visited[i]){dfs(i,graph,visited,stack);}}//将栈中的元素弹出即为拓扑排序结果List<Integer>result=newArrayList<>();while(!stack.isEmpty()){result.add(stack.pop());}returnresult;}privatestaticvoiddfs(intnode,List<List<Integer>>graph,boolean[]visited,Stack<Integer>stack){visited[node]=true;//遍历所有邻居for(intneighbor:graph.get(node)){if(!visited[neighbor]){dfs(neighbor,graph,visited,stack);}}//当前节点所有邻居处理完后,将当前节点压入栈stack.push(node);}publicstaticvoidmain(String[]args){//构建相同的图intnumVertices=6;List<List<Integer>>graph=newArrayList<>();for(inti=0;i<numVertices;i++){graph.add(newArrayList<>());}graph.get(5).add(2);graph.get(5).add(0);graph.get(4).add(0);graph.get(4).add(1);graph.get(2).add(3);graph.get(3).add(1);//拓扑排序List<Integer>sortedOrder=topologicalSort(numVertices,graph);System.out.println("拓扑排序结果(DFS算法):"+sortedOrder);}}```**解释**:1.**DFS遍历**:对每个未访问节点进行深度优先搜索。2.**后序压栈**:在DFS过程中,当一个节点的所有邻居都被访问后,将该节点压入栈中。3.**结果构建**:完成所有DFS后,栈中元素从栈顶到栈底的顺序即为拓扑排序的逆序,将其弹出即可得到拓扑排序。###两种算法比较|特性|Kahn算法|DFS算法||--------------|------------------------------|-----------------------------||**原理**|基于入度,队列|基于深度优先搜索||**检测环**|通过计数节点是否全部访问|通过递归栈检测环(需额外处理)||**性能**|O(V+E)|O(V+E)||**适用性**|易于理解,需要入度信息|递归实现简洁|###环检测在Kahn算法中,如果结果列表中的节点数少于总节点数,则说明存在环。在DFS算法中,如果递归过程中遇到正在访问的节点(即递归栈中的节点),则说明存在环(但上述DFS实现未显式检测环,需要修改以检测环)。**DFS算法改进(带环检测)**:```javapublicstaticList<Integer>topologicalSortWithCycleDetection(intnumVertices,List<List<Integer>>graph){boolean[]visited=newboolean[numVertices];boolean[]inStack=newboolean[numVertices];//用于检测环Stack<Integer>stack=newStack<>();for(inti=0;i<numVertices;i++){if(!visited[i]){if(dfsWithCycleDetection(i,graph,visited,inStack,stack)){thrownewIllegalStateException("Graphhasacycle");}}}//弹出栈中元素得到拓扑排序List<Integer>result=newArrayList<>();while(!stack.isEmpty()){result.add(stack.pop());}returnresult;}privatestaticbooleandfsWithCycleDetection(intnode,List<List<Integer>>graph,boolean[]visited,boolean[]inStack,Stack<Integer>stack){if(inStack[node]){returntrue;//存在环}if(visited[node]){returnfalse;}visited[node]=true;inStack[node]=true;for(intneighbor:graph.get(node)){if(dfsWithCycleDetection(neighbor,graph,visited,inStack,stack)){returntrue;}}inStack[node]=false;stack.push(node);returnfalse;}```###应用场景-任务调度(依赖关系)-编译顺序(源文件依赖)-课程安排-工作流程管理§§相关问题§§:1.如何检测有向图中是否存在环?2.除了拓扑排序,还有哪些图排序算法?3.拓扑排序在软件工程中有哪些实际应用?4.如何处理带权重的有向无环图(DAG)的拓扑排序?</think># 拓扑排序Java实现详解 拓扑排序是针对有向无环图(DAG)的一种线性排序算法,使得对于任何有向边(u, v),节点u在排序中出现在节点v之前。以下是完整的Java实现: ```java import java.util.*; public class TopologicalSort { // 使用邻接表表示有向图 private Map<String, List<String>> graph = new HashMap<>(); private List<String> vertices = new ArrayList<>(); // 添加顶点 public void addVertex(String vertex) { if (!graph.containsKey(vertex)) { graph.put(vertex, new ArrayList<>()); vertices.add(vertex); } } // 添加有向边 public void addEdge(String from, String to) { addVertex(from); addVertex(to); graph.get(from).add(to); } // Kahn算法实现拓扑排序(基于入度) public List<String> topologicalSortKahn() { // 计算每个顶点的入度 Map<String, Integer> inDegree = new HashMap<>(); for (String vertex : vertices) { inDegree.put(vertex, 0); } for (List<String> edges : graph.values()) { for (String to : edges) { inDegree.put(to, inDegree.get(to) + 1); } } // 初始化入度为0的队列 Queue<String> queue = new LinkedList<>(); for (String vertex : vertices) { if (inDegree.get(vertex) == 0) { queue.add(vertex); } } // 处理队列 List<String> result = new ArrayList<>(); int count = 0; while (!queue.isEmpty()) { String current = queue.poll(); result.add(current); count++; // 减少邻居的入度 for (String neighbor : graph.get(current)) { inDegree.put(neighbor, inDegree.get(neighbor) - 1); if (inDegree.get(neighbor) == 0) { queue.add(neighbor); } } } // 检查是否存在环 if (count != vertices.size()) { throw new IllegalStateException("Graph has a cycle, topological sort not possible"); } return result; } // DFS实现拓扑排序 public List<String> topologicalSortDFS() { Stack<String> stack = new Stack<>(); Set<String> visited = new HashSet<>(); Set<String> recursionStack = new HashSet<>(); for (String vertex : vertices) { if (!visited.contains(vertex)) { if (!dfs(vertex, visited, recursionStack, stack)) { throw new IllegalStateException("Graph has a cycle"); } } } List<String> result = new ArrayList<>(); while (!stack.isEmpty()) { result.add(stack.pop()); } return result; } private boolean dfs(String vertex, Set<String> visited, Set<String> recursionStack, Stack<String> stack) { visited.add(vertex); recursionStack.add(vertex); for (String neighbor : graph.get(vertex)) { if (!visited.contains(neighbor)) { if (!dfs(neighbor, visited, recursionStack, stack)) { return false; } } else if (recursionStack.contains(neighbor)) { return false; // 发现环 } } recursionStack.remove(vertex); stack.push(vertex); return true; } // 获取所有可能的拓扑排序(回溯法) public List<List<String>> getAllTopologicalOrders() { List<List<String>> results = new ArrayList<>(); Map<String, Integer> inDegree = new HashMap<>(); // 初始化入度 for (String vertex : vertices) { inDegree.put(vertex, 0); } for (List<String> edges : graph.values()) { for (String to : edges) { inDegree.put(to, inDegree.get(to) + 1); } } boolean[] visited = new boolean[vertices.size()]; List<String> currentOrder = new ArrayList<>(); backtrack(inDegree, visited, currentOrder, results); return results; } private void backtrack(Map<String, Integer> inDegree, boolean[] visited, List<String> currentOrder, List<List<String>> results) { // 找到所有当前入度为0且未访问的顶点 List<String> candidates = new ArrayList<>(); for (int i = 0; i < vertices.size(); i++) { String vertex = vertices.get(i); if (inDegree.get(vertex) == 0 && !visited[i]) { candidates.add(vertex); } } // 如果没有候选顶点,检查是否完成排序 if (candidates.isEmpty()) { if (currentOrder.size() == vertices.size()) { results.add(new ArrayList<>(currentOrder)); } return; } // 尝试每个候选顶点 for (String candidate : candidates) { int index = vertices.indexOf(candidate); visited[index] = true; currentOrder.add(candidate); // 减少邻居的入度 Map<String, Integer> changes = new HashMap<>(); for (String neighbor : graph.get(candidate)) { changes.put(neighbor, inDegree.get(neighbor)); inDegree.put(neighbor, inDegree.get(neighbor) - 1); } // 递归 backtrack(inDegree, visited, currentOrder, results); // 回溯:恢复状态 for (Map.Entry<String, Integer> entry : changes.entrySet()) { inDegree.put(entry.getKey(), entry.getValue()); } currentOrder.remove(currentOrder.size() - 1); visited[index] = false; } } // 测试用例 public static void main(String[] args) { TopologicalSort ts = new TopologicalSort(); // 构建课程依赖图 ts.addEdge("数学基础", "离散数学"); ts.addEdge("离散数学", "数据结构"); ts.addEdge("程序设计", "数据结构"); ts.addEdge("数据结构", "算法分析"); ts.addEdge("程序设计", "面向对象"); ts.addEdge("面向对象", "软件工程"); ts.addEdge("离散数学", "数据库"); ts.addEdge("数据库", "软件工程"); System.out.println("=== Kahn算法拓扑排序 ==="); List<String> kahnResult = ts.topologicalSortKahn(); System.out.println(kahnResult); System.out.println("\n=== DFS算法拓扑排序 ==="); List<String> dfsResult = ts.topologicalSortDFS(); System.out.println(dfsResult); System.out.println("\n=== 所有可能的拓扑排序 ==="); List<List<String>> allOrders = ts.getAllTopologicalOrders(); for (int i = 0; i < Math.min(5, allOrders.size()); i++) { System.out.println("排序 " + (i+1) + ": " + allOrders.get(i)); } System.out.println("总排序数: " + allOrders.size()); // 测试环检测 System.out.println("\n=== 环检测测试 ==="); ts.addEdge("软件工程", "程序设计"); // 创建环 try { ts.topologicalSortKahn(); } catch (IllegalStateException e) { System.out.println("Kahn算法检测到环: " + e.getMessage()); } try { ts.topologicalSortDFS(); } catch (IllegalStateException e) { System.out.println("DFS算法检测到环: " + e.getMessage()); } } } ``` ## 算法详解 ### 1. Kahn算法(基于入度) ```java public List<String> topologicalSortKahn() { // 计算入度 Map<String, Integer> inDegree = new HashMap<>(); // ...初始化入度 // 初始化入度为0的队列 Queue<String> queue = new LinkedList<>(); while (!queue.isEmpty()) { String current = queue.poll(); result.add(current); // 减少邻居的入度 for (String neighbor : graph.get(current)) { inDegree.put(neighbor, inDegree.get(neighbor) - 1); if (inDegree.get(neighbor) == 0) { queue.add(neighbor); } } } // 检查环 if (count != vertices.size()) { throw new IllegalStateException("Graph has a cycle"); } } ``` **算法步骤**: 1. 计算每个节点的入度(指向该节点的边数) 2. 将所有入度为0的节点加入队列 3. 从队列中取出节点,加入结果列表 4. 将该节点的所有邻居的入度减1 5. 如果邻居入度变为0,加入队列 6. 重复3-5直到队列为空 7. 如果结果节点数 ≠ 总节点数,说明存在环 **时间复杂度**: O(V+E),其中V是顶点数,E是边数 ### 2. DFS算法 ```java public List<String> topologicalSortDFS() { Stack<String> stack = new Stack<>(); Set<String> visited = new HashSet<>(); Set<String> recursionStack = new HashSet<>(); for (String vertex : vertices) { if (!visited.contains(vertex)) { if (!dfs(vertex, visited, recursionStack, stack)) { throw new IllegalStateException("Graph has a cycle"); } } } // 反转栈得到结果 List<String> result = new ArrayList<>(); while (!stack.isEmpty()) { result.add(stack.pop()); } return result; } private boolean dfs(String vertex, Set<String> visited, Set<String> recursionStack, Stack<String> stack) { visited.add(vertex); recursionStack.add(vertex); for (String neighbor : graph.get(vertex)) { if (!visited.contains(neighbor)) { if (!dfs(neighbor, visited, recursionStack, stack)) return false; } else if (recursionStack.contains(neighbor)) { return false; // 发现环 } } recursionStack.remove(vertex); stack.push(vertex); return true; } ``` **算法步骤**: 1. 使用DFS遍历图 2. 维护递归栈检测环 3. 当节点完成所有邻居访问后,将其压入栈 4. 最终栈中元素从顶到底即为拓扑排序结果(需要反转) 5. 如果发现环立即终止 **特点**: - 使用递归栈检测环 - 后序遍历完成节点处理 - 结果需要反转 ### 3. 所有可能的拓扑排序(回溯法) ```java public List<List<String>> getAllTopologicalOrders() { // 初始化入度 Map<String, Integer> inDegree = new HashMap<>(); backtrack(inDegree, visited, currentOrder, results); return results; } private void backtrack(Map<String, Integer> inDegree, boolean[] visited, List<String> currentOrder, List<List<String>> results) { // 找到所有当前入度为0且未访问的顶点 List<String> candidates = new ArrayList<>(); // 尝试每个候选顶点 for (String candidate : candidates) { // 选择候选顶点 // 减少邻居入度 // 递归 // 回溯:恢复状态 } } ``` **算法思想**: 1. 在每一步选择所有入度为0的候选节点 2. 对于每个候选节点: - 将其加入当前路径 - 减少其所有邻居的入度 - 递归处理 - 回溯:恢复邻居入度并从路径中移除 3. 当所有节点都加入路径时,保存结果 **应用场景**: - 任务调度中寻找所有可能的执行顺序 - 课程安排中的多种选择 - 优化并行执行计划 ## 拓扑排序的应用场景 1. **任务调度系统**: - 确定任务执行顺序 - 识别任务依赖关系 - 优化并行执行计划 2. **构建系统**: - 确定编译顺序(如Makefile) - 解决文件依赖关系 - 增量构建优化 3. **课程安排**: - 确定课程选修顺序 - 规划学习路径 - 识别先修课程要求 4. **数据流分析**: - 确定数据处理顺序 - 优化流水线执行 - 识别循环依赖 5. **电路设计**: - 确定逻辑门评估顺序 - 优化信号传播路径 - 检测组合逻辑环 ## 环检测的重要性 拓扑排序只能应用于有向无环图(DAG)。在实现中,我们通过两种方式检测环: 1. Kahn算法:如果最终排序节点数 < 总节点数,说明存在环 2. DFS算法:通过递归栈检测后向边(back edge) 环检测在以下场景至关重要: - 避免无限循环的任务调度 - 防止编译过程中的循环依赖 - 确保课程安排不出现不可能满足的先修条件 - 识别数据处理流水线中的死循环
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符  | 博主筛选后可见
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值