home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Frozen Fish 1: Amiga
/
FrozenFish-Apr94.iso
/
bbs
/
alib
/
d1xx
/
d128
/
wkeys.lha
/
wKeys
/
mSort.c
< prev
next >
Wrap
C/C++ Source or Header
|
1988-01-02
|
11KB
|
291 lines
/*
* MSORT.C version 1.0 18-Nov-1986
*
* A general-purpose implementation of a merge-sort for linked lists.
*
* Written for the Commodore Amiga 1000, version 1.1
* using Lattice C v3.03 by Davide P. Cervone
*
* The merge-sort algorithm takes advantage of the fact that it is
* easy to combine two sorted lists into one sorted list: just keep
* taking the smaller item off the top of the two lists and add it
* to the bottom of the new list until the original lists are empty.
*
* The sort algorithm first places each item to be sorted in its
* own list (one item long). Pairs of these singleton lists are merged
* in the manner described above, and we are left with half as many lists
* as before, each two items long. Pairs of these lists are combined to
* form half as many lists again, each four items long. This process is
* repeated until only one list remains. This is the final, sorted list.
*
* The merge-sort is ideal for linked lists, since it does not require
* random access look-ups (which are difficult in linked-lists). It does
* not require extra "work-space", so no memory is wasted. Finally, its
* result is a simple, linked-list structure, ready to use (unlike some sort
* methods that result in trees or other structures were it is dificult to
* get from one item to the next).
*
* The merge-sort is fairly efficient. In it's worst case, it takes
* n * (log(n) - 1) + 1 comparisons to sort n items (where the log()
* function is the log to base 2). Thus, for 1024 items, the maximum
* number of comparisons is 1024*(10-1)+1 = 9217, or approximately 9
* comparisons per item. And that's its worst case! Compare this to
* a worst case of n*(n-1)/2 for some popular sorts (e.g., the tree sort).
* The worst case for the tree sort for 1024 items is 523776 compares!
*
* In its best case, the merge-sort takes n * log(n) / 2 comparisons.
* For our example of n=1024, that's 5120 comparisons. For the tree sort,
* the minimum is approximately n * (log(n) - 2) comparisons, for a
* minimum of 8192 comparisons for 1024 items.
*
* Note that the maximum comparisons that will ever occur with the merge-sort
* is no more than n more than the minimum number possible with the tree
* sort.
*
* While these numbers look impressive, there is one drawback: the merge-
* sort is heavily weighted toward its worst case, since the merge sort skips
* comparisons only when one list runs out of items more than one item before
* the other. In practice, the average number of comparisons made by the
* merge-sort is a consistant 96% of the maximum number. This gives you a very
* acurate estimate of the number of comparisons (hence the time it takes) to
* sort a list of a particular size. Since the maximum value is so close to
* the most probable value, you don't have to worry about pathelogic cases
* slowing down your sort routine (for a tree sort, the "pathelogic" case
* is a pre-sorted list, which may not be an uncommon case).
*
* In a comparison against the tree sort using random data (the kind the
* tree sort handles best), the merge-sort was found to take 96% of its
* maximum number of sorts, while the tree sort averaged about 38% more than
* it's minimum (there was considerably more variation with the tree sort).
* Overall, the tree sort was found to take a consitant 25% more comparisons
* than the merge-sort on lists of length 300 items to 10000 items. For
* 100 items, the difference drops off to about 15%, and for 10 items, the two
* sorts are about the same, with the merge-sort comming out a little bit
* ahead (one or two comparisons out of an average of 23).
*
* All in all, the merge-sort is an effective, fast, and very useful sort
* that compares favorably with other sort algorithms.
*/
#include <exec/types.h>
#define ADD_TO_NEWLIST(l) (newlist = (newlist->Next = l))
/*
* The linked list is a double-linked list (there are pointers to both
* the previous and the following item). The end of the list is marked
* by NULL pointers.
*
* The pointers should appear at the top of the structure you intend to
* sort, so if you are sorting a linked list of people's names, you might
* use a structure like the following in your main program:
*
* struct ListItem
* {
* struct ListItem *Next;
* struct ListItem *Prev;
* char FirstName[30];
* char MiddleInitial;
* char LastName[50]
* };
*
* The pointers must be the first fields so that mSort() and Merge() can
* find the pointers without having to know anything about the structure
* of the data you are sorting.
*/
/*
* The #ifndef is so that this module can be #included into the program
* that calls mSort(), rather than requiring it to be linked in separately.
* To avoid a conflicting definition error, #define NextList to be the
* name of the pointer to the previous item as it is declared in your
* program (for the example given above, user #define NextList Prev).
* be sure that the #define and the struct ListItem declaration both
* appear before the #include for this module
*/
#ifndef NextList
struct ListItem
{
struct ListItem *Next;
struct ListItem *NextList;
};
#endif
/*
* These are the compare and dispose functions passed to mSort() (see
* mSort() for more details), and are assigned to global variables so that
* they don't have to be passed to mMerge() every time it is called.
*/
static int (*mCompare)() = NULL;
static void (*mDispose)() = NULL;
/*
* mMerge()
*
* combines two sorted linked lists into one sorted linked list, using
* the mCompare() function to determine the sorting, and the dispose()
* function to remove duplicate values.
*
* list1 a pointer to the first linked list
* list2 a pointer to the second linked list
*/
struct ListItem *
mMerge(list1,list2)
struct ListItem *list1, *list2;
{
static struct ListItem top_item;
static struct ListItem *newlist, *temp;
static int result;
top_item.Next = top_item.NextList = NULL;
newlist = &top_item;
/*
* While there are still items in each list, compare the top items in the
* lists. Link the smaller one to the end of the new, combined list, and
* pop the item off the top of the old list. If the top items are equal,
* then add one of them to the new list, and if there is a dispose function,
* get rid of the other one, otherwise add the second one into the list
* as well.
*
* When one of the lists is exauseted, add any remaining items from
* the other list onto the end of the result list, and return a pointer
* to the final, sorted list.
*/
while (list1 != NULL && list2 != NULL)
{
result = (*mCompare)(list1,list2);
if (result < 0)
{
ADD_TO_NEWLIST(list1);
list1 = list1->Next;
}
else if (result > 0)
{
ADD_TO_NEWLIST(list2);
list2 = list2->Next;
}
else
{
ADD_TO_NEWLIST(list1);
list1 = list1->Next;
if (mDispose == NULL)
{
ADD_TO_NEWLIST(list2);
list2 = list2->Next;
} else {
temp = list2;
list2 = list2->Next;
temp->Next = temp->NextList = NULL;
(*mDispose)(temp);
}
}
}
if (list1 != NULL) ADD_TO_NEWLIST(list1);
else if (list2 != NULL) ADD_TO_NEWLIST(list2);
return(top_item.Next);
}
/*
* mSort()
*
* sorts a linked list of items of any sort, using a user-provided comparison
* routine. Duplicate items will be removed if the dispose routine is
* provided. The result list will be a doubly-linked list (pointers go both
* forward and backward) that is sorted. mSort() returns a pointer to
* the top of the result list.
*
* cur_item a pointer to the begining of the original, unsorted
* list.
* compare() a pointer to a function that compares two
* ListItems and returns a negative number if
* the first item is smaller, zero if they are
* equal, and a positive number if the first
* item is larger than the second. Compare()
* should take two parameters, the pointers
* to the ListItems that are to be compared.
* dispose() disposes of a duplicate ListItem. Dispose()
* should accept one parameter, the pointer to
* the ListItem to be removed. Dispose() should
* free any memory allocated to the ListItem.
* This list item will not appear in the final
* linked list. If dispose in NULL, then
* duplicate items are retained in the list.
*/
struct ListItem *
mSort(cur_item,compare,dispose)
struct ListItem *cur_item;
int (*compare)();
void (*dispose)();
{
static struct ListItem *first_item, *next_item;
/*
* Put the compare and dispose routines where mMerge() can find them
*/
mCompare = compare;
mDispose = dispose;
first_item = NULL;
if (cur_item != NULL)
{
/*
* Put each item in the original list into a separate list. Use the
* NextList field to link the individual lists into a list (a linked list
* of linked lists). Link the end of the list to the begining so we get
* a ring rather than a list.
*/
first_item = cur_item;
do
{
next_item = cur_item->Next;
cur_item->Next = NULL;
cur_item->NextList = (next_item != NULL)? next_item : first_item;
cur_item = next_item;
} while (cur_item != NULL);
/*
* While there's more than one list left in the ring (i.e., the list
* following the current one is not itself), then merge the current list
* with the one following it (keep track of the first list after the ones
* we're combining so we can link the result of the merge back into
* the ring). Finally, if there were more than two lists in the ring
* (i.e., if the current list is neither equal to the one preceding it nor
* equal to the one after the list with which it was combined), then
* link the result list into the ring, otherwise link the result list
* to itself (since there were only two lists in the ring, their
* merger should leave us with only one). Move down the ring, so we
* can merge the next two lists in the ring.
*/
while ((cur_item = first_item->NextList) != first_item)
{
next_item = cur_item->NextList->NextList;
cur_item = mMerge(cur_item,cur_item->NextList,compare,dispose);
if (cur_item == first_item || cur_item == next_item)
{
cur_item->NextList = cur_item;
} else {
first_item->NextList = cur_item;
cur_item->NextList = next_item;
}
first_item = cur_item;
}
/*
* When there's only one list left, traverse the list, setting the
* reverse pointers so that the list is properly linked both directions.
*/
first_item->NextList = NULL;
while (cur_item->Next != NULL)
{
cur_item->Next->NextList = cur_item;
cur_item = cur_item->Next;
}
}
return(first_item);
}