tsort

function tsort( graph ) --> order, err

Description

Returns a linear ordering of the nodes in graph. It works only for directed acyclic graphs. The implemention of the topological sorting is using a depth-first search implementations.

Parameters

graph

The graph that should be ordered.

Return Values

order

The linear ordering of graph as array table or nil if the graph contains cycles.

err

nil or a message if the graph contains cycles.

Code

--ZFUNC-tsort-v1
local function tsort( graph ) --> order, err
   local order = {}

   local know = {}
   for node in pairs( graph ) do
      if not know[ node ] then
         local seen = {}
         local stack = {}
         table.insert( stack, node )
         while #stack > 0 do
            local current_node = stack[ #stack ]
            if know[ current_node ] then
               table.remove( stack )
            else
               seen[ current_node ] = true
               local added = false
               for next_node in pairs( graph[ current_node ] ) do
                  if not know[ next_node ] then
                     if seen[ next_node ] then
                        return nil, "Graph contains a cycle."
                     end
                     added = true
                     table.insert( stack, next_node )
                  end
               end
               if not added then
                  table.remove( stack )
                  know[ current_node ] = true
                  table.insert( order, 1, current_node )
               end
            end
         end
      end
   end

   return order
end

return tsort

Examples

local t = require( "tapered" )
local tsort = require( "tsort" )
-- util functions
local addedge = require( "addedge" )
local buildgraph = require( "buildgraph" )
local occurasc = require( "occurasc" )

local edges = {
   { "a", "x" },
   { "x", "c" },
   { "x", "z" },
   { "c", "z" }
}
local g = buildgraph( {}, edges )
-- a --> x --> c
--       \      \
--        -----> --> z
local order, err = tsort( g )

t.ok( occurasc( order, "a", "x", "c", "z" ) )
t.is( err, nil )

addedge( g, "z", "a" ) -- adding circle a->x->z->a
order, err = tsort( g )
t.is( order, nil )
t.is( err, "Graph contains a cycle." )

g = buildgraph( { "a", "b", "c" }, {} )
addedge( g, "a", "b" )
addedge( g, "a", "c" )
addedge( g, "b", "c" )
-- a ----------> c
--  \         /
--   --> b -->
order, err = tsort( g )
t.ok( occurasc( order, "a", "b", "c" ) )
t.is( err, nil )

t.done()

See Also