|
DataMuseum.dkPresents historical artifacts from the history of: DKUUG/EUUG Conference tapes |
This is an automatic "excavation" of a thematic subset of
See our Wiki for more about DKUUG/EUUG Conference tapes Excavated with: AutoArchaeologist - Free & Open Source Software. |
top - metrics - downloadIndex: T t
Length: 9014 (0x2336) Types: TextFile Names: »tsort.cc«
└─⟦a05ed705a⟧ Bits:30007078 DKUUG GNU 2/12/89 └─⟦cc8755de2⟧ »./libg++-1.36.1.tar.Z« └─⟦23757c458⟧ └─⟦this⟧ »libg++/etc/tsort.cc«
// Date: Sun, 18 Sep 88 15:57:31 -0700 // From: "Douglas C. Schmidt" <schmidt%crimee.ics.uci.edu@ORION.CF.UCI.EDU> // I wrote the following useful // tool to perform topological sorting in O(n + m) average-time (n == # // of vertices, m == # of edges). Feel free to add it, or any of its // components parts to the libg++ library in any shape or form you // desire. // If you happen to have Andrew Koenig's Summer USENIX '88 article on // Associative Arrays in C++ it is interesting to compare his solution to // mine. Basically, my version, which uses hashing as opposed to his AVL // trees, should be *much* faster on the average (it would be interesting // to compare the two). In fact, if you test the code below against the // standard UNIX tsort routine, you should be *amazed* at how much faster // it is (several orders of magnitude on large input files)! #include <stream.h> #include <String.h> #define NIL(TYPE) ((TYPE *)0) class Vertex_Node; // forward decls class Assoc_Array; class Assoc_Iter; // Note: Most of the pointer types, like the **Hash_Table should probably be // typedeffed. Unfortunately, this breaks the G++ 1.27 compiler! class Adjacency_Node { // Defines and implements the adjacency list abstraction that contains the list // of vertices that are incident to given vertex. Note that we store Vertex_Node // pointers here, rather than vertex *Names*. This allows us to find a particular // vertex in the Hash_Table in O(1) time, without having to perform the usual // hash lookup (compare this with Andrew Koenig's implementation from the Summer // USENIX '88 proceedings!) private: Vertex_Node *Item_Ptr; Adjacency_Node *Next_Ptr; public: Adjacency_Node (Vertex_Node *New_Item,Adjacency_Node *Head): Item_Ptr (New_Item), Next_Ptr (Head) { // The constructor simply orders the adjacency list as a stack. ; } Vertex_Node *Item (void) { return this->Item_Ptr; } Adjacency_Node *Next (void) { return this->Next_Ptr; } }; class Vertex_Node { // The Hash_Table is composed of these Vertex_Nodes. Each Vertex_Node contains // an adjacency list of successor nodes, and an In_Degree count. Each operation // is extremely concise and self-explanatory, with the exception of the overloaded // ``+='' operator, which essentially means ``Add the successor node to the successor // list (OK, so this is an abuse of overloading!) private: friend class Vertex_Node& Assoc_Array::Lookup_In_Hash_Table (String& Index); friend class Vertex_Node *Assoc_Iter::operator ()(void); int In_Degree; String Name; // Using (char *) here is faster, but less abstract! Adjacency_Node *Succ_List; Vertex_Node *Next; public: Vertex_Node (String Item,Vertex_Node *N = NIL (Vertex_Node)): In_Degree (0), Name (Item), Next (N), Succ_List (NIL (Adjacency_Node)) { } int In_Degree_Count (void) { return In_Degree; } Adjacency_Node *Get_Succ_List (void) { return Succ_List; } Vertex_Node& operator ++ (void) { In_Degree++; return *this; } int operator -- (void) { return --In_Degree; } Vertex_Node& operator += (Vertex_Node& Succ_Node) { Succ_List = new Adjacency_Node (&Succ_Node,Succ_List); return *this; } String Item (void) { return Name; } friend ostream& operator << (ostream& Stream,Vertex_Node& Output) { return Stream << Output.In_Degree; } }; class Assoc_Array { // Here's the main data structure. It is simply a Hash_Table of Vertex_Nodes. // I make use of hashpjw from the libg++ builtin library. Nothing too tricky // here.... I overload the [] operator to provide the classic ``associative // array'' touch. private: friend class Vertex_Node *Assoc_Iter::operator ()(void); int Total_Size; int Current_Size; Vertex_Node **Hash_Table; Vertex_Node& Lookup_In_Hash_Table (String& Index) { int Location = hashpjw (Index) % Total_Size; Vertex_Node *Head = Hash_Table[Location]; if (!Head) { Current_Size++; return *(Hash_Table[Location] = new Vertex_Node (Index)); } else { while (Head) { if (Head->Name == Index) return *Head; Head = Head->Next; } Current_Size++; return *(Hash_Table[Location] = new Vertex_Node (Index,Hash_Table[Location])); } } public: Assoc_Array (int Size = 100): Current_Size (0),Total_Size (Size) { Hash_Table = new Vertex_Node *[Size]; bzero (Hash_Table,Size * sizeof (Vertex_Node *)); } Vertex_Node& operator[](String Index) { return Lookup_In_Hash_Table (Index); } int Array_Size (void) { return Current_Size; } int Max_Size (void) { return Total_Size; } }; class Assoc_Iter { // This is a neat class, which implements an iterator for the Hash_Table used // for the associative array. It would be faster to search the table directly, // without using an iterator, but this is a neat feature of C++ that fits too // nicely into the overall abstraction to pass up! private: Assoc_Array *Assoc; Vertex_Node *Curr; int Curr_Table_Pos; public: Assoc_Iter (Assoc_Array& Array,int Items): Assoc (&Array), Curr (NIL (Vertex_Node)), Curr_Table_Pos (Items - 1) { } Vertex_Node *operator ()(void) { if (Curr_Table_Pos <= 0) // we're at the end of the table, quit. return NIL (Vertex_Node); else if (!Curr) { // need to find a new bucket list to search. while (Curr_Table_Pos >= 0 && !Assoc->Hash_Table[Curr_Table_Pos]) Curr_Table_Pos--; if (Curr_Table_Pos < 0) return NIL (Vertex_Node); else Curr = Assoc->Hash_Table[Curr_Table_Pos]; } Vertex_Node *Temp = Curr; if (!(Curr = Curr->Next)) // try to update Curr for next call. Curr_Table_Pos--; return Temp; } }; class Node_Stack { // Yawn.... This is a very simple stack abstraction. I probably should have // used some generic routine from libg++.... ;-) Doesn't perform error checking. private: int Top; int Max_Node_Stack_Size; Vertex_Node **Node_Stack_Items; public: Node_Stack (int Size): Top (Size), Max_Node_Stack_Size (Size) { Node_Stack_Items = new Vertex_Node *[Size]; } int Empty () { return Top >= Max_Node_Stack_Size; } void Push (Vertex_Node *Item) { Node_Stack_Items[--Top] = Item; } Vertex_Node *Pop (void) { return Node_Stack_Items[Top++]; } }; const int Default_Size = 200; main (int argc,char *argv[]) { Assoc_Array A ((argc > 1) ? atoi (argv[1]) : Default_Size); String Prev; String Succ; // The use of ``+='' below is rather obscure. It simply means ``prepend the // address of A[Succ] to the adjacency list of A[Prev]. See the comment in // the member definition for class Vertex_Node. Note that the ++ operator // has also been overloaded. This allows concise, extremely efficient mechanism // to enter the Prev and Succ items into the associative array. while (cin >> Prev >> Succ) if (Prev == Succ) // Just record existence of identical tokens. A[Prev]; else A[Prev] += ++A[Succ]; Assoc_Iter Next (A,A.Max_Size ()); Node_Stack Stack (A.Array_Size ()); Vertex_Node *Node_Ptr; // Iterate through all the items in the associative array, performing the // typical topological sort trick of pushing all ``sources'' onto a stack. // This gives us an order of magnitude win over the standard UNIX tsort // routine, which has quadratic average time complexity (in the number of // verticies!!!!). while (Node_Ptr = Next ()) if (Node_Ptr->In_Degree_Count () == 0) Stack.Push (Node_Ptr); // The following code is straight-forward, but the one line with the expression // --(*List->Item ()) == 0 is rather cryptic. It is simply dereferencing a pointer // to a Vertex_Node, and then passing this by reference to the overloaded -- // operator from the Vertex_Node class (yes, it *is* easy to abuse this // technique!). for (int Items = 0; !Stack.Empty (); Items++) { Node_Ptr = Stack.Pop (); cout << Node_Ptr->Item () << "\n"; for (Adjacency_Node *List = Node_Ptr->Get_Succ_List (); List; List = List->Next ()) if (--*List->Item () == 0) Stack.Push (List->Item ()); } if (Items != A.Array_Size ()) cout << "Error, directed graph has a cycle!\n"; return 0; }