home *** CD-ROM | disk | FTP | other *** search
/ Source Code 1992 March / Source_Code_CD-ROM_Walnut_Creek_March_1992.iso / usenet / altsrcs / 1 / 1685 < prev    next >
Internet Message Format  |  1990-12-28  |  21KB

  1. From: wayned@wddami.spoami.com (Wayne Diener)
  2. Newsgroups: alt.sources
  3. Subject: Sorting Algorithm
  4. Message-ID: <wayned.0092@wddami.spoami.com>
  5. Date: 16 Aug 90 23:00:00 GMT
  6.  
  7. I have been unable to find any references to the sorting algorithm
  8. used in this program.  If anyone has seen it elsewhere, I would 
  9. appreciate hearing about it.  None of my "guru" programmer friends,
  10. my professors (I'm a hardware type who went back to college to learn
  11. a little software) or a literature search have turned up an equivalent
  12. so I'm posting it here to see if any one already knows of it.  If it's
  13. original with me, I claim a copywrite on it and release it for use
  14. by anyone.  I'm certain it can be improved - feel free to do so, but
  15. send me a hard copy via US mail, OK?
  16.  
  17. I have done empirical comparisons of this sort against bubble,
  18. insertion, selection and queuemerge sort.  It's a lot faster than
  19. the first three but slower than the queuemerge sort, however, it
  20. does the sort "in-place" (requires very little additional memory,
  21. other than a few more variables).  I'm not really terribly proficient
  22. at "Big O" analysis, but it LOOKS like it might be O(N*log(N))
  23. instead of O(N^2).  Anyone want to analyse it?  
  24.  
  25. I 'trimmed' this file from the original form (massive documentation
  26. for class requirements) to include only what I think is really 
  27. useful.  I don't think I cut out anything important, but I can't
  28. be 100% sure since I haven't re-compiled.  The sorting algorithm itself 
  29. should be okay.  I compiled and ran the program using Turbo C, 
  30. cc on a Sun 386i and under Ultrix, so it should work in most environments. 
  31.  
  32. The sorting is accomplished using a binary search of a linked list
  33. to find the the proper insertion point (just running up and down
  34. the pointers and dividing the list in half each time) and then 
  35. moving the pointers to change an item's location.
  36.  
  37. Have fun, and not too many flames, OK? (Remember this was a class
  38. assignment (for string manipulation actually) and I had to demonstrate
  39. some concepts other than just the sorting.)
  40.  
  41.  
  42. X-----------------------  CUT  ----------------------------------------X
  43. /***********************************************************************
  44.    A "Binary Insertion Sorting Technique for Linked-Lists"
  45.    Wayne D. Diener
  46.    South 3415 Myrtle
  47.    Spokane, WA  99223
  48.    (509) 535-4670
  49.  
  50.     This program reads a file of input words (as you might type
  51.     them in with any programming editor), prints the word count
  52.     and the list of words, sorts the list and then prints the
  53.     list again.
  54.  
  55.     (Oh, yea...  Bi_In_Sort() is the actual function)
  56.  
  57.  
  58.    *inp         FILE          Used to read the characters from the input
  59.                               file.
  60.    ch           char          Used for input to "hold" the screen long
  61.                               enough to see the results.
  62.    *mnlst       main_list    A pointer to the header for the list.
  63.  
  64. ************************************************************************/
  65.  
  66. #include <stdio.h>
  67. #include <fcntl.h>
  68. typedef char data;
  69.  
  70. typedef struct node              /* Each node contains a character. */
  71. {
  72.    data character;
  73.    struct node *next_letter;
  74.    struct node *prev_letter;
  75. } node;
  76.  
  77. typedef struct header            /* Each header starts a word. */
  78. {
  79.    int letter_count;
  80.    struct node *word_head;
  81.    struct node *word_tail;
  82.    struct header *next_word;
  83.    struct header *prev_word;
  84. } header;
  85.  
  86. typedef struct main_list         /* This is the main list header. */
  87. {
  88.    int word_count;
  89.    struct header *head_word;
  90.    struct header *tail_word;
  91. } main_list;
  92.  
  93.  
  94. main(argc,argv)
  95.  
  96.    int argc;
  97.    char *argv[];
  98.  
  99. {
  100.    FILE       *inp,*fopen();
  101.    int        ch;
  102.    main_list  *mnlst;
  103.    void       read_list();
  104.    void       print_list();
  105.    void       erase_list();
  106.    void       Bi_In_Sort();
  107.    int        compare_words();
  108.  
  109.    if (argc != 2)
  110.    {
  111.       printf("Error.  Useage:  %s filename",argv[0]);
  112.       exit(1);
  113.    }
  114.  
  115.    if ((inp = fopen(argv[1],"r")) == NULL)
  116.    {
  117.       printf("Could not open %s for input.",argv[1]);
  118.       exit(1);
  119.    }
  120.    mnlst = (main_list *) malloc (sizeof(main_list));
  121.  
  122.    read_list(mnlst,inp);  /* Read the words into a list. */
  123.  
  124.    fclose(inp);     /* Close the input file. */
  125.  
  126.    printf(" Word count = %d\n",mnlst->word_count);
  127.  
  128.    printf("\n The input word list printed forward is:\n\n");
  129.    print_list(mnlst->tail_word,0);  /*It's recursive so start at wrong end*/
  130.  
  131.    Bi_In_Sort(mnlst,compare_words);              /* Sort the list. */
  132.  
  133.    printf("\n The sorted word list is:\n\n");
  134.    print_list(mnlst->tail_word,0);
  135.    printf("\n\n\n Press return to continue.\n");
  136.    scanf("%c",&ch);      /* Leave the screen up until a <cr> */
  137.    erase_list(mnlst);    /* Clean up the memory. */
  138.  
  139. }
  140.  
  141. void
  142. print_word(ptr)
  143.    header *ptr;
  144. /***********************************************************************
  145.    print_word() - accepts a pointer to the header of a word it prints
  146.    out all the characters contained in the list at that node and then
  147.    prints a space character.
  148.  
  149.    variables used:
  150.    name         type                     Description
  151.    -------------------------------------------------------------------
  152.    *p           node           Points at the characters to print.
  153.    i            int            A loop control variable that counts letters.
  154. ***********************************************************************/
  155. {
  156.    node *p = ptr->word_head;
  157.    int i = ptr->letter_count;
  158.    while (i-- != 0)
  159.    {
  160.  
  161.       printf("%c",p->character);
  162.       p = p->prev_letter;
  163.    }
  164.    printf("%c",' ');     /* Put a space after it. */
  165. }
  166.  
  167. void
  168. print_list(point,dir)
  169.    header *point;
  170.    int   dir;
  171. /***********************************************************************
  172.    print_list() - accepts a pointer to one of the word nodes on the list
  173.    and a variable that determines which direction to print (forward or
  174.    reverse).  It then traverses the list of words recursively and prints
  175.    the word contained at each node.
  176.  
  177.    variables used:
  178.    name         type                     Description
  179.    -------------------------------------------------------------------
  180.    none
  181. ***********************************************************************/
  182.                          /* If dir = 0 we'll print the list normally. */
  183.                          /* If dir != 0 we'll print backward. */
  184. {                        /* It works either direction. */
  185.    void Print_word();
  186.  
  187.    if((dir ? point->prev_word : point->next_word) != NULL)
  188.       print_list((dir ? point->prev_word : point->next_word),dir);
  189.    print_word(point);
  190. }
  191.  
  192. void
  193. erase_list(list)
  194.    main_list *list;
  195. /***********************************************************************
  196.    erase_list() -  accepts a pointer to the main word list.  It traverses
  197.    the list erase each word list associated with the node, goes to the
  198.    next node and erases the previous header.  Finally it erase the last
  199.    word node and then the header for the main list.
  200.  
  201.    variables used:
  202.    name         type                     Description
  203.    -------------------------------------------------------------------
  204.    *p            header     Points at the word node to be erased.
  205. ***********************************************************************/
  206. {
  207.    header  *p = list->head_word;
  208.    void erase_word();
  209.    while ( p != NULL)  /* p is not passed list->tail_word*/
  210.    {
  211.       erase_word(p);        /* Erase the word. */
  212.       p = p->prev_word;     /* Go to the next word. */
  213.       if (p != NULL)
  214.          free(p->next_word);   /* Free the previous word header. */
  215.    }
  216.    free(list->tail_word);  /* Free the last word header. */
  217.    free(list);             /* Free the list header. */
  218. }
  219.  
  220. void
  221. erase_word(word_node)
  222.    header *word_node;       /* word_node is the header for the word. */
  223. /***********************************************************************
  224.    erase_word() -  Accepts a pointer to a word node.  It traverses the 
  225.    list of character nodes and frees the memory associated with each
  226.    character.
  227.  
  228.    variables used:
  229.    name         type                     Description
  230.    -------------------------------------------------------------------
  231.    *p           header        A helper pointer.
  232.    *q           header        The pointer used to free the memory.
  233.    i            int           A loop counter used to count the letters.
  234. ***********************************************************************/
  235. {
  236.    node *p,*q = word_node->word_head;       /* p points at the letters. */
  237.    int i;
  238.    for (i=0;i < word_node->letter_count;i++)
  239.    {
  240.        p = q->prev_letter;   /* Save the next letter pointer. */
  241.        free(q);              /* Free the letter. */
  242.        q = p;                /* Point at the next letter. */
  243.    }
  244. }
  245.  
  246. int
  247. compare_words(first,second)
  248.    header   *first,*second;
  249. /***********************************************************************
  250.    compare_words() -  Accepts two pointer to word headers.  It compares
  251.    the letters contained at each node of the word in succession.  If it
  252.    encounters a letter in one word that is greater than the corrsponding
  253.    letter in the other word, it returns the appropriate value.  If the end
  254.    of either (or both) word(s) is reached a determination of the longer
  255.    of the two words is attempted by comparing the lengths of the words.
  256.    If the lengths are different, the function returns the appropriate
  257.    value, if the word lengths are the same, it returns the "equal value".
  258.  
  259.    variables used:
  260.    name         type                     Description
  261.    -------------------------------------------------------------------
  262.    *p           header        Points to the header of the first word
  263.                               to compare.
  264.    *q           header        Points to the header of the second word
  265.                               to compare.
  266.  
  267. ***********************************************************************/
  268. /*  if first > second, return 0.  If second > first, return 2
  269. if first = second, return 1 */
  270.  
  271. {
  272.    node *p = first->word_head,*q = second->word_head;
  273.  
  274.    while ((p != NULL) && (q != NULL))  /* As long as letters are there. */
  275.    {
  276.       if ( p->character > q->character)        /* First > Second. */
  277.          return(0);
  278.       else if ( p->character < q->character)   /* Second > First. */
  279.          return(2);
  280.       else                                     /* Equal so far! */
  281.       {
  282.          p = p->prev_letter;     /* Go to the next letters. */
  283.          q = q->prev_letter;
  284.       }
  285.    }
  286.  /* To get here, one or both of the words is out of letters and they
  287.  are equal to this point. */
  288.  
  289.    if (first->letter_count > second->letter_count)       /* First > */
  290.       return(0);
  291.    else if (first->letter_count < second->letter_count)  /* Second > */
  292.       return(2);
  293.    else return(1);          /* The words are equal. */
  294. }
  295.  
  296. void
  297. Bi_In_Sort(big_list,compare)
  298.    main_list *big_list;
  299.    int    (*compare)();
  300.  
  301. /***********************************************************************
  302.    Bi_In_Sort() - Accepts a pointer to the header of a list to process.
  303.    First, a sorted portion is created at the end of the list that is 2
  304.    items long then a loop is entered that repeatedly takes the next item
  305.    at the "head" of the list and uses a binary search of the items in the
  306.    sorted portion to determine the correct location for the new item.
  307.    The new item is then removed from the head of the list and inserted
  308.    at the appropriate location.  This process is repeated until the last
  309.    item has been processed.
  310.  
  311.    variables used:
  312.    name         type                     Description
  313.    -------------------------------------------------------------------
  314.    test         int           Used as a flag to signal the instance
  315.                               where the new item should be inserted
  316.                               prior to the present smallest member of
  317.                               the list.
  318.    count        int           Used to keep track of the number of words
  319.                               already in the sorted portion of the list.
  320.    middle       int           Used as a counter control variable to
  321.                               determine how far "up" or "down" the list
  322.                               to travel during the binary search.
  323.    i            int           The count control variable for list traversal.
  324.    up           int           Used as a boolean control variable to determine
  325.                               if the next movement on the list should be
  326.                               "up" the list or "down" the list.
  327.    *current     header        The pointer that is moved "up" and "down" the
  328.                               list while searching for the proper insertion
  329.                               location.
  330.    *newitem     header        A pointer that is used as a "handle" during
  331.                               the movement/insertion of the head of the list.
  332.    *sortbound   header        A pointer that points to the "lowest" item of
  333.                               the sorted portion of the list.
  334.  
  335. ***********************************************************************/
  336. {
  337.    int      test,count=1,middle,i,up;
  338.    header   *current,*newitem,*sortbound;
  339.    void     insert();
  340.  
  341.    if (big_list->word_count > 1)  /* A one item list is already sorted. */
  342.    {
  343.       current = big_list->tail_word->next_word;
  344.       if ( (*compare)(current,big_list->tail_word) == 0)
  345.       {
  346.          current->next_word->prev_word = big_list->tail_word;
  347.          big_list->tail_word->next_word = current->next_word;
  348.          current->next_word = big_list->tail_word;
  349.          big_list->tail_word->prev_word = current;
  350.          current->prev_word = NULL;
  351.          big_list->tail_word = current;
  352.       }
  353.       /*   The sorted part is now two items long. */
  354.       sortbound = big_list->tail_word->next_word;
  355.       do
  356.       {
  357.          up = 1;                         /*"Outside loop" initializations.*/
  358.          newitem = big_list->head_word;
  359.          count++;
  360.          middle = (count +1) / 2;
  361.          current = sortbound;
  362.          test = 0;
  363.          do
  364.          {
  365.             for ( i=0; i < middle ; i++)
  366.             /* Go to the appropriate "middle". */
  367.             {                          /* Either up or down the list. */
  368.                current = up ?   current->prev_word : current->next_word;
  369.                if (current == sortbound)
  370.                {
  371.                   test = 1;          /* If we get to the sort boundary, */
  372.                   break;             /* we have to quit. */
  373.                }
  374.             }
  375.             if (((*compare)(newitem,current) == 0) && current != NULL)
  376.             {
  377.                if (current == big_list->tail_word)
  378.                /* Place the item at the tail. */
  379.                {
  380.                   big_list->head_word = newitem->prev_word;
  381.                   big_list->head_word->next_word = NULL; /* the new head */
  382.                   newitem->prev_word = NULL; /* the new end of the list */
  383.                   newitem->next_word = big_list->tail_word;
  384.                   big_list->tail_word->prev_word = newitem;
  385.                   big_list->tail_word = newitem;
  386.                   break;
  387.                   /* The sortbound stays in the same place. */
  388.                }
  389.                else
  390.                   up = 1;     /* Otherwise we have to look further "up". */
  391.             }
  392.             /*  The case of inserting after the tail_word is finished. */
  393.             else if(test)   /* To get here, newitem <= current */
  394.                             /* and current = sortbound */
  395.                             /* so we insert before current */
  396.                             /* and move sortbound to the new item. */
  397.             {
  398.                if ( current == newitem->prev_word)
  399.                   return;   /* This is the actual finishing point. */
  400.                else
  401.                {
  402.                   insert(big_list,newitem,current);
  403.                   sortbound = newitem;
  404.                   break;
  405.                }
  406.             }
  407.             /*  Inserting before sortbound is finished. */
  408.             /*  To get here, newitem <= current and somewhere in the middle*/
  409.             else if ( (*compare)(newitem,current->next_word) <= 1)
  410.             {
  411.                insert(big_list,newitem,current);
  412.                break;
  413.             }
  414.             else
  415.             /*  newitem is strictly less than current->next, so: */
  416.             /*  go down the list.  */
  417.                up = 0;
  418.        middle /=2;
  419.        if (middle == 0)
  420.           middle++;
  421.        }
  422.        while (1);
  423.     }
  424.     while (sortbound != big_list->head_word);
  425.     }
  426. }
  427.  
  428. void
  429. read_list(ml,inp)
  430.    main_list *ml;
  431.    FILE     *inp;
  432. {
  433.    node       *dat;
  434.    header     *list;
  435.    char       a, ch = 32;    /* This makes the initial while loop work. */
  436.    void       print_word();
  437.  
  438.    while ((ch == 32) || (ch == 13) || (ch == 10))
  439.       ch = getc(inp);  /* This halts formation of words from */
  440.    ungetc(ch,inp);   /* leading spaces, etc. (no letters). */
  441.  
  442.    if((ch = getc(inp)) != EOF)
  443.    {
  444.       dat = (node *) malloc (sizeof(node));
  445.       list = (header *) malloc (sizeof(header));
  446.       ml->head_word = list;      /* Points to the head of the whole list. */
  447.       ml->tail_word = list;      /* Points to the tail of the whole list. */
  448.       ml->word_count = 1;        /* There's at least one word in the list. */
  449.  
  450.       list->letter_count = 1;    /* There's at least one letter in the word. */
  451.       list->word_head = dat;     /* This points at the first letter. */
  452.       list->word_tail = dat;     /* This points at the last letter. */
  453.       list->next_word = NULL;    /* This is the only word so far. */
  454.       list->prev_word = NULL;
  455.  
  456.  
  457.       dat->character = ch;  /* This is the first letter of the first word. */
  458.       dat->next_letter = NULL;     /* It's also the only letter right now. */
  459.       dat->prev_letter = NULL;
  460.  
  461.       while(ch != EOF)
  462.       {
  463.          if((ch=getc(inp)) != EOF)
  464.          {
  465.             if((ch == 32) || (ch == 13) || (ch == 10))
  466.             {                   /* New word.  Make a new word header node. */
  467.                                 /* The following while, ungetc and if(ch..) were also necessary
  468.                                         if allowing <cr> and <lf> as word seperators. */
  469.                while ((ch == 32) || (ch == 13) || (ch == 10))
  470.                                                 ch = getc(inp);  /* This halts formation of words from */
  471.                ungetc(ch,inp);   /* extra spaces, etc. (no letters). */
  472.                                  /* but put the last character back. */
  473.                if (ch == EOF)    /* This protects agains a final word */
  474.                                                 break;         /* being generated at EOF. */
  475.                list = (header *) malloc (sizeof(header));
  476.                /* link it into the main_list. */
  477.                list->prev_word = NULL;               /* It's the new end. */
  478.                ml->tail_word->prev_word = list;
  479.                list->next_word = ml->tail_word;
  480.                ml->tail_word = list;    /* Adjust the end of the word list. */
  481.                ml->word_count++;        /* Increment the word count. */
  482.                list->letter_count = 0;  /* No letters in the word yet. */
  483.             }
  484.             else if((ch != 10) && (ch != 13))
  485.             /* Disallow <cr> and <lf> as actual parts of the words. */
  486.             {
  487.                dat = (node *) malloc (sizeof(node));
  488.                dat->prev_letter = NULL; /* This is the new end of the word*/
  489.                dat->character = ch;     /* Place the letter in the node. */
  490.                if (list->letter_count == 0)  /* No letters in the word yet*/
  491.                {
  492.                   list->word_head = dat;   /* Point at the end of the word*/
  493.                   dat->next_letter = NULL; /* The first letter has no next*/
  494.                }
  495.                else
  496.                {
  497.                   list->word_tail->prev_letter = dat; /* Link to end word */
  498.                   dat->next_letter = list->word_tail;
  499.                }
  500.                list->letter_count++;  /* Increment the letter count. */
  501.                list->word_tail = dat; /* The new end of the word. */
  502.                }
  503.          }
  504.       }
  505.    }
  506. }
  507.  
  508. void
  509. insert(bg_lst,new,cur)      /* These are the common statements required*/
  510.    main_list *bg_lst;       /* for any insertion prior to current pointer*/
  511.    header    *new,*cur;
  512. /***********************************************************************
  513.    insert() - Accepts the pointers to the main list and the current
  514.     and newitem and performs an isertion of the new item prior to the
  515.     current location.
  516.  
  517.    variables used:
  518.    name         type                     Description
  519.    -------------------------------------------------------------------
  520.    none
  521. ***********************************************************************/
  522. {
  523.  
  524.    bg_lst->head_word = new->prev_word;
  525.    bg_lst->head_word->next_word = NULL;
  526.    new->next_word = cur->next_word;
  527.    cur->next_word->prev_word = new;
  528.    cur->next_word = new;
  529.    new->prev_word = cur;
  530. }
  531.