DataMuseum.dk

Presents historical artifacts from the history of:

DKUUG/EUUG Conference tapes

This is an automatic "excavation" of a thematic subset of
artifacts from Datamuseum.dk's BitArchive.

See our Wiki for more about DKUUG/EUUG Conference tapes

Excavated with: AutoArchaeologist - Free & Open Source Software.


top - metrics - download
Index: T t

⟦efb9bd03e⟧ TextFile

    Length: 9014 (0x2336)
    Types: TextFile
    Names: »tsort.cc«

Derivation

└─⟦a05ed705a⟧ Bits:30007078 DKUUG GNU 2/12/89
    └─⟦cc8755de2⟧ »./libg++-1.36.1.tar.Z« 
        └─⟦23757c458⟧ 
            └─⟦this⟧ »libg++/etc/tsort.cc« 

TextFile

// 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;
}