154 lines
5.0 KiB
ObjectPascal
154 lines
5.0 KiB
ObjectPascal
program ToposortTask;
|
|
{$mode delphi}
|
|
uses
|
|
SysUtils, Generics.Collections;
|
|
|
|
type
|
|
TDigraph = class(TObjectDictionary<string, THashSet<string>>)
|
|
procedure AddNode(const s: string);
|
|
procedure AddArc(const s, t: string);
|
|
function AdjList(const s: string): THashSet<string>;
|
|
{ returns True and the sorted sequence of nodes in aOutSeq if is acyclic,
|
|
otherwise returns False and the first found cycle; uses DFS }
|
|
function TryToposort(out aOutSeq: TStringArray): Boolean;
|
|
end;
|
|
|
|
procedure TDigraph.AddNode(const s: string);
|
|
begin
|
|
if not ContainsKey(s) then
|
|
Add(s, THashSet<string>.Create);
|
|
end;
|
|
|
|
procedure TDigraph.AddArc(const s, t: string);
|
|
begin
|
|
AddNode(s);
|
|
AddNode(t);
|
|
if s <> t then
|
|
Items[s].Add(t);
|
|
end;
|
|
|
|
function TDigraph.AdjList(const s: string): THashSet<string>;
|
|
begin
|
|
if not TryGetValue(s, Result) then
|
|
Result := nil;
|
|
end;
|
|
|
|
function TDigraph.TryToposort(out aOutSeq: TStringArray): Boolean;
|
|
var
|
|
Parents: TDictionary<string, string>;// stores the traversal tree as pairs (Node, its predecessor)
|
|
procedure ExtractCycle(const BackPoint: string; Prev: string);
|
|
begin // just walk backwards through the traversal tree, starting from Prev until BackPoint is encountered
|
|
with TList<string>.Create do begin
|
|
Add(Prev);
|
|
repeat
|
|
Prev := Parents[Prev];
|
|
Add(Prev);
|
|
until Prev = BackPoint;
|
|
Add(Items[0]);
|
|
Reverse; //this is required since we moved backwards through the tree
|
|
aOutSeq := ToArray;
|
|
Free;
|
|
end
|
|
end;
|
|
var
|
|
Visited, // set of already visited nodes
|
|
Closed: THashSet<string>;// set of nodes whose subtree traversal is complete
|
|
Counter: SizeInt = 0;
|
|
function Dfs(const aNode: string): Boolean;// True means successful sorting,
|
|
var // False - found cycle
|
|
Next: string;
|
|
begin
|
|
Visited.Add(aNode);
|
|
for Next in AdjList(aNode) do
|
|
if not Visited.Contains(Next) then begin
|
|
Parents.Add(Next, aNode);
|
|
if not Dfs(Next) then exit(False);
|
|
end else
|
|
if not Closed.Contains(Next) then begin//back edge found(i.e. cycle)
|
|
ExtractCycle(Next, aNode);
|
|
exit(False);
|
|
end;
|
|
Closed.Add(aNode);
|
|
aOutSeq[Counter] := aNode;
|
|
Inc(Counter);
|
|
Result := True;
|
|
end;
|
|
var
|
|
Node: string;
|
|
begin
|
|
SetLength(aOutSeq, Count);
|
|
Visited := THashSet<string>.Create;
|
|
Closed := THashSet<string>.Create;
|
|
Parents := TDictionary<string, string>.Create;
|
|
Result := True;
|
|
for Node in Keys do
|
|
if not Visited.Contains(Node) then
|
|
if not Dfs(Node) then begin
|
|
Result := False;
|
|
break;
|
|
end;
|
|
Visited.Free;
|
|
Closed.Free;
|
|
Parents.Free;
|
|
end;
|
|
|
|
{ expects text separated by line breaks }
|
|
function ParseRawData(const aData: string): TDigraph;
|
|
var
|
|
Line, Curr, Node: string;
|
|
FirstTerm: Boolean;
|
|
begin
|
|
Result := TDigraph.Create([doOwnsValues]);
|
|
for Line in aData.Split([LineEnding], TStringSplitOptions.ExcludeEmpty) do begin
|
|
FirstTerm := True;
|
|
for Curr in Line.Split([' '], TStringSplitOptions.ExcludeEmpty) do
|
|
if FirstTerm then begin
|
|
Node := Curr;
|
|
Result.AddNode(Curr);
|
|
FirstTerm := False;
|
|
end else
|
|
Result.AddArc(Node, Curr);
|
|
end;
|
|
end;
|
|
|
|
procedure TrySort(const aData: string);
|
|
var
|
|
g: TDigraph;
|
|
Sorted: TStringArray;
|
|
begin
|
|
g := ParseRawData(aData);
|
|
if g.TryToposort(Sorted) then
|
|
WriteLn('success: ', LineEnding, string.Join(', ', Sorted))
|
|
else
|
|
WriteLn('circular dependency: ', LineEnding, string.Join('->', Sorted));
|
|
g.Free;
|
|
end;
|
|
|
|
const
|
|
ExampleData =
|
|
'des_system_lib std synopsys std_cell_lib des_system_lib dw02 dw01 ramlib ieee' + LineEnding +
|
|
'dw01 ieee dw01 dware gtech' + LineEnding +
|
|
'dw02 ieee dw02 dware' + LineEnding +
|
|
'dw03 std synopsys dware dw03 dw02 dw01 ieee gtech' + LineEnding +
|
|
'dw04 dw04 ieee dw01 dware gtech' + LineEnding +
|
|
'dw05 dw05 ieee dware' + LineEnding +
|
|
'dw06 dw06 ieee dware' + LineEnding +
|
|
'dw07 ieee dware' + LineEnding +
|
|
'dware ieee dware' + LineEnding +
|
|
'gtech ieee gtech' + LineEnding +
|
|
'ramlib std ieee' + LineEnding +
|
|
'std_cell_lib ieee std_cell_lib' + LineEnding +
|
|
'synopsys';
|
|
var
|
|
Temp: TStringArray;
|
|
|
|
begin
|
|
TrySort(ExampleData);
|
|
WriteLn;
|
|
//let's add a circular dependency
|
|
Temp := ExampleData.Split([LineEnding], TStringSplitOptions.ExcludeEmpty);
|
|
Temp[1] := Temp[1] + ' dw07';
|
|
Temp[7] := Temp[7] + ' dw03';
|
|
TrySort(string.Join(LineEnding, Temp));
|
|
end.
|