From c0f562102369d55e5a6b67edf68c298d824a7ba4 Mon Sep 17 00:00:00 2001 From: Dominik Braun Date: Mon, 5 Jun 2023 20:40:36 +0200 Subject: [PATCH] Return to old implementation of `TopologicalSort` --- CHANGELOG.md | 5 +++++ dag.go | 53 +++++++++++++++++++++++++++++++++++++++++++++++++--- 2 files changed, 55 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 5f0199db..825bbfdc 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,11 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +## [0.22.1] - 2023-06-05 + +### Fixed +* Fixed `TopologicalSort` to retain its original performance. + ## [0.22.0] - 2023-05-24 ### Added diff --git a/dag.go b/dag.go index 8400c2e9..b83f805c 100644 --- a/dag.go +++ b/dag.go @@ -18,9 +18,56 @@ import ( // TopologicalSort only works for directed acyclic graphs. This implementation // works non-recursively and utilizes Kahn's algorithm. func TopologicalSort[K comparable, T any](g Graph[K, T]) ([]K, error) { - return StableTopologicalSort(g, func(_, _ K) bool { - return false - }) + if !g.Traits().IsDirected { + return nil, fmt.Errorf("topological sort cannot be computed on undirected graph") + } + + predecessorMap, err := g.PredecessorMap() + if err != nil { + return nil, fmt.Errorf("failed to get predecessor map: %w", err) + } + + queue := make([]K, 0) + + for vertex, predecessors := range predecessorMap { + if len(predecessors) == 0 { + queue = append(queue, vertex) + } + } + + order := make([]K, 0, len(predecessorMap)) + visited := make(map[K]struct{}) + + for len(queue) > 0 { + currentVertex := queue[0] + queue = queue[1:] + + if _, ok := visited[currentVertex]; ok { + continue + } + + order = append(order, currentVertex) + visited[currentVertex] = struct{}{} + + for vertex, predecessors := range predecessorMap { + delete(predecessors, currentVertex) + + if len(predecessors) == 0 { + queue = append(queue, vertex) + } + } + } + + gOrder, err := g.Order() + if err != nil { + return nil, fmt.Errorf("failed to get graph order: %w", err) + } + + if len(order) != gOrder { + return nil, errors.New("topological sort cannot be computed on graph with cycles") + } + + return order, nil } // StableTopologicalSort does the same as [TopologicalSort], but takes a function