* @zh 使用 DFS 进行拓扑排序 * @en Topological sort using DFS * * @zh DFS 算法特点: * - 实现简单 * - 递归方式,栈溢出风险(极端情况)
(
items: T[],
resolveId: (id: string) => string
)
| 251 | * - 递归方式,栈溢出风险(极端情况) |
| 252 | */ |
| 253 | function dfsSort<T extends IDependable>( |
| 254 | items: T[], |
| 255 | resolveId: (id: string) => string |
| 256 | ): TopologicalSortResult<T> { |
| 257 | const itemMap = new Map<string, T>(); |
| 258 | for (const item of items) { |
| 259 | itemMap.set(item.id, item); |
| 260 | } |
| 261 | |
| 262 | const sorted: T[] = []; |
| 263 | const visited = new Set<string>(); |
| 264 | const visiting = new Set<string>(); // 用于检测循环 |
| 265 | const cycleIds: string[] = []; |
| 266 | |
| 267 | const visit = (item: T): boolean => { |
| 268 | if (visited.has(item.id)) return true; |
| 269 | if (visiting.has(item.id)) { |
| 270 | cycleIds.push(item.id); |
| 271 | return false; // 发现循环 |
| 272 | } |
| 273 | |
| 274 | visiting.add(item.id); |
| 275 | |
| 276 | for (const dep of item.dependencies || []) { |
| 277 | const depId = resolveId(dep); |
| 278 | const depItem = itemMap.get(depId); |
| 279 | if (depItem && !visit(depItem)) { |
| 280 | cycleIds.push(item.id); |
| 281 | return false; |
| 282 | } |
| 283 | } |
| 284 | |
| 285 | visiting.delete(item.id); |
| 286 | visited.add(item.id); |
| 287 | sorted.push(item); |
| 288 | return true; |
| 289 | }; |
| 290 | |
| 291 | for (const item of items) { |
| 292 | if (!visited.has(item.id)) { |
| 293 | visit(item); |
| 294 | } |
| 295 | } |
| 296 | |
| 297 | return { |
| 298 | sorted, |
| 299 | hasCycles: cycleIds.length > 0, |
| 300 | cycleIds: cycleIds.length > 0 ? [...new Set(cycleIds)] : undefined |
| 301 | }; |
| 302 | } |
| 303 | |
| 304 | /** |
| 305 | * @zh 拓扑排序(统一入口) |
no test coverage detected