LiteC and Lists
Part II
By
3DSki
3/2007
Introduction
First, you’ll be lost if you haven’t gone through the part I and II of this “LiteC and Lists’ series. If you haven’t gone though at least part I, please go there first.
In the first part we covered the basics of what a one-way linked list is and how to manipulate its items. In the second part, we made our list processing code more generic so we wouldn’t have to re-write a lot of the code every time we developed a new game that made use of different data types. The goal of that tutorial was really to show how a linked list could be split into two separate lists. However, to do this we needed some kind of list management scheme set up, so we would have a way of handling the new list when it was created. We buried all the lower level workings away in functions, for use by higher level function that would serve as our real API.
In this tutorial, we want to look at the problem of sorting and find a good way to address it, in the case of a one-way linked list. Unlike the tutorial on splitting a list, we should be able to jump right in to directly address the top… ya!
Sorting Lists
First off, I will admit that I’m not a sorting guru! There are chapters in books on the topic of different sorting methods, full statistical analysis on the performance of each of the methods. We are not going to have that kind of discussion here! However, we can certainly analyze what kind of data we want to sort, look at the strengths and weakness of some common methods, in general, and work this through to a solution. Its more like learning how to analyze your data in the context of the language you’re using and how the data is being represented in your application. So, though I’m not the computer science sorting guru, I’m well qualified to walk you through the analysis and design. Let’s begin!
The Problems
We want to sort… that’s our problem. Not exactly. We want to sort a linked list. The code for possible ways of sorting a linked list can become very burdensome! In comparison, sorting simple primitive values such as integers in an array are much more seems much more manageable. Why? The good ol’ index! With an array, you can always quickly retrieve items with their indices. This is not the case with a linked list!
The real problem with a linked list is, in our case, it is unidirectional… you can only move forward through it. With a linked list, we can not simply subtract 1 from the current index to gain access to the previous item. The best we can do is look at the items down the line in the list and its hard to jump to random locations that you might want to jump to. In general, this is often an operation that sorting algorithms expect to be able to do to achieve their goal in a short amount of time. Many of the methods expected a jumbled mass of data that they will work on in mass. Is this necessarily what we need to achieve, or ask our sort processing to do? If not, what is the other alternative? … it’s so simple, some of you might even think of it. An insert sort!
The Insertion Sort
What is an insert sort? Basically, as I recall it, it is sorting the items in a lists as you add them… how simple is that! When we add an item, we compare that item with the items in the list, walking from item to item. And, with a linked list, we can look at the current and next items. We the knowledge we’ve gain with managing the items of a linked list, we should be able to insert the item where it belongs with this knowledge.
Comparisons
We have one other problem that is dependent on the data being used. How do we compare the items? Is the data you want to sort numerical or text? Better yet, if you’re working with records (a “struct”), will the key field be numerical or text? So, we have to determine the type of data we want to compare, because it’s the difference between a simple “if-greater-than” or “if-less-than” and using a string comparison function to do the comparison. In the last tutorial, we looked at how we can incorporate C’s strcmp() into LiteC to do string comparison, so there’s one place we can look to find out how to jump this hurdle.
Our Reformulated Goal
Now that we’ve analyzed all our problems we can change our goal from, “sorting data” to, “Sorting data, as numerical or text, as each item is added to a one-way linked list.” Concerning the “numerical or text” portion of this goal, it would be much nicer if the numeric or text comparison wasn’t hard-wired into the code. In part two of these tutorials we saw an example of how we can pass in a custom comparison function into another function for it to use when we wanted to find an item. This is another perfect opportunity to use this technique!
Setting Up
First, we need to put together some test data; list and item structures, some code to build our test lists, some functions to do text and numeric comparisons and finally, our sorting logic.
Our Structures:
#define TEXT
#ifdef TEXT
typedef struct item
{
char letter[2];
struct item* next;
} ITEM;
#endif
#ifdef NUMERIC
typedef struct item
{
int number;
struct item* next;
} ITEM;
#endif
typedef struct list
{
ITEM* start;
ITEM* end;
} LIST;
The “typedefed” structures above should look familiar to you by now. There is nothing special about these, other than the fact that I’ve given them each generic names for the purpose of reusing our logic between test cases; numeric and text sorts. However, you will notice that I’ve used some precompiler directives to help us use the code we want for each test. The directives will look for either NUMERIC or TEXT to choose which ITEM typedef to use. Next, we need to use a similar technique to construct our test lists:
char* strcpy(char* a, char* b);
int strcmp(char* a, char* b);
strcpy = DefineApi("mscvrt!strcpy");
strcmp = DefineApi("mscvrt!strcmp");
LIST* list = {start = NULL;end=NULL;}
void main()
{
#ifdef NUMERIC
ITEM* one = {number = 1; next = NULL;}
ITEM* two = {number = 2; next = NULL;}
ITEM* three = {number = 3; next = NULL;}
#endif
#ifdef TEXT
ITEM* A = NULL;
ITEM* B = NULL;
ITEM* C = NULL;
A = malloc( sizeof(ITEM));
strcpy(A.letter, "A");
A.next = NULL;
B = malloc( sizeof(ITEM));
strcpy(B.letter, "B");
B.next = NULL;
C = malloc( sizeof(ITEM));
strcpy(C.letter, "C");
C.next = NULL;
free(A);
free(B);
free(C);
#endif
}
In the code listing above, you’ll see that we have declared one LIST* and three ITEM* variables. In one case our items will contain a numeric values, and text in the other. Having built this foundation, let’s work on our comparison functions.
Making Comparisons
We need two comparison functions. One function will need to handle numeric data while the other will be used to handle text. Our comparison functions need to look at three possible outcomes. In one case, the item we’re adding will be less than the item it is being compared to. In the second case, the item may be greater than the item it’s being compared to. Finally, the item may be found to equal the item it is being compared to. The C language’s strcmp() function, which we can import for comparing text, handles these situations as follows. It returns a negative value if the first parameter is less than it’s second. It returns 0 if the values passed in are equal to one another. Finally, it returns a positive value if the first parameter is greater than the second:
strcmp(“A”, “B”); // = < 0
strcmp(“A”, “A”); // = 0
strcmp(“B”, “A”); // = >0
Since strcmp() uses these scheme, we should use the same for our numeric comparison, as well.
This was all pretty easy, but now we have to decide how to create the function pointer used to call each of our comparison functions when we need them called. This is a little tricky. At this point in time, it would be uncommon for most to think that the numeric comparison function should take the two integers to compare, while the text comparison function will simply take to character strings. If you want to do a “quick and dirty” test, this would be true. But, in our case these functions are to be shared and called by one single function pointer. This means that the return value and the parameter types must be of the same type! We know that we will be passing back an “int”, but what do you think we have as our reusable data type for the parameters of both functions? …ITEM*! So, let’s create our comparison function prototype for our comparison function pointer:
int compareItems(ITEM* in, ITEM* current);
I’ve named my function pointer “compareItems”, because that’s exactly what we’re doing. Next, I call the first parameter “in”, since it will be expected to be the item we are adding to the list, to be compared with the existing ones. I’ve named the second parameter “current”, since it is expected to be the current item from the list to compare “in” with.
Now, let’s create our actual functions, which should be a piece of cake:
#ifdef NUMERIC
int compareNumeric(ITEM* in, ITEM* current)
{
return in.number – current.number;
}
#endif
#ifdef TEXT
int compareText(ITEM* in, ITEM* current)
{
return strcmp(in.letter, current.letter);
}
#endif
Notice that in my compareNumeric() I don’t actually do any comparisons! All we really need the numeric result we expect, so I simple subtract the current value from that of “in”. If in.number is equal to current.number then 0 will be returned. If in.number is larger than a positive value will be returned. If in.number is less than current.number, then the result will be a negative number. Both functions are short and sweet!
The Insertion Sort Function
Now were ready to tie things all together. This function should be that difficult either. From all the experience you’ve gained from the past tutorials, you should realize that we will be putting some of all we’ve learned together. Our code will be very similar to that for inserting an item, but will include our comparison to test to see where the insertion takes place. But first, as you might know by now, I’m big at looking at special cases before I start coding. We really cannot begin to code effectively until we really understand the conditions under which we will be making comparisons and doing the items insertions! “All we have to do is compare ‘in’ with ‘current’ and insert in before or after ‘current’.” … partially true, but not the big picture.
Now, I said partially true, because this is one situation that you have to handle. However, it might take you a little while to see a problem with this. Remember, in this type of linked list, we only know the current and next item. If you want to remember the previous one (to insert the item before the current) then you’ll have to save this info in your code! What other cases can you think of for the insert/sort? What do we do when the list is empty? What do we do if the item hasn’t been inserted and we reach the end of the list? What needs to be done if the value we want to insert is greater than the current, but less than that of the next item in the list? Try to think of how you would tie the solutions together into our “insertionSort” function. Let’s start with a skeleton and build on it:
int insertionSort( ITEM* item, LIST* list, void* comparitor)
{
if ( item != NULL && list != NULL && comparator != NULL)
{
…
return 1;
}
return 0;
}
My skeleton code starts with some basic requirements. We need the item, the list and a pointer to one or our comparison functions in order to do the insertion sort, so these are our parameters. Since they’re vital, we test to make sure we have valid values for these.
The next internal bit we need is the common logic we’ve used in the past to loop through each of the items of our list:
int insertionSort( ITEM* item, LIST* list, void* comparitor)
{
ITEM* current;
ITEM* previous;
if ( item != NULL && list != NULL && comparator != NULL)
{
current = list.start;
while( current != NULL )
{
…
return 1;
}
}
return 0;
}
Above, I’ve taken into account our need for “current”. Based on our earlier discussion, I’ve added a “previous” ITEM pointer as, as well. I also moved the successful return (return 1) up into the loop, because this is were we will really know when the insertion takes place and we can use this statement to exit the loop and the function.
Now, we’re ready to begin look at how we compare and add our items to the list. The easiest case to handle is adding an item to an empty list. Since the list is empty, there are no comparisons to be made and no items to loop through. So in this case, we simply set the list’s “start” to the item and return 1 from the function. Let’s add this logic to the function:
int insertionSort( ITEM* item, LIST* list, void* comparitor)
{
ITEM* current;
ITEM* previous;
if ( item != NULL && list != NULL && comparator != NULL)
{
if ( list.start != NULL )
{
current = list.start;
while( current != NULL )
{
…
return 1;
}
} else {
list.start = item;
return 1;
}
}
return 0;
}
When add items to the list, there are very few occassion where the list will be empty, so I check for the list not being empty first. Most importantly, the logic to add the item to an empty list should definitely not go inside of the while-loop. If we put the test for “start == NULL in the while loop, we would be making an unnecessary test almost every time we added an item to the list.
Now, let’s say we added our first item. If there is only 1 item in the list, we know that the second item will need to be placed before or after the first item. Here, we will have to begin using the “previous” pointer and our comparison function. We’ve not initialized “previous”, so we will need to add this to our logic. We also need to add the logic to our loop so that we set “previous” to “current” and move on to the next item for comparison if we’re not able to insert the item during the current iteration of the loop:
ITEM* current;
ITEM* previous;
if ( item != NULL && list != NULL && comparator != NULL)
{
previous = NULL;
if ( list.start != NULL )
{
compareItems = comparator;
current = list.start;
while( current != NULL )
{
if ( compareItems( item, current ) < 0 ) {
// if we need to add item to the start
// of the list…
if ( previous == NULL )
{
item.next = start;
list.start = item;
return 1;
}
}
previous = current;
current = current.next;
}
} else {
list.start = item;
return 1;
}
}
return 0;
}
First, an quit important, you need to notice that I did not initialize “previous” when I declared it. If you declare and initialize a variable there LiteC might assume that the value is “static”. This would mean that “previous” would still be the value we had set it to prior to leaving the function when we return to it. We’ve not talked about static variables, but this is how they behave, and this is not what we want in most situations, so it’s probably a good idea not to declare and initialize variables at the same time when you’re in a function!
Next, you see we’re finally using our function pointer to call our comparison function. Here, we simply set the function pointer equal to the function pointer we passed in. Once set, we use it to call the function. Here, we’re testing to see if the item we’ve passed in is less than the current item. If it is, then we need to add the item before the current one. However, if “current” is the very first item, then “previous” will be NULL, which let’s us know that we need to add the item to the start of the list. This means that the added item’s “next” needs to be set to the list’s original “start” and that the list’s “start” needs to be set to our new item. Remember, we are simply juggling addresses that point to our data items to make this so.
Knowing where to add the code so far has been pretty easy. Now, I think it is more difficult. However, we can work through this, but we have to shift gears a bit. To do this we need to realize that what we have tested for so far is what I would call, and “odd ball” case. We’ve only tested to see if the item needs to be added to the beginning of the list. In all other situations, “previous” will not be NULL. If it is not NULL, and we are still in the loop making another test, then our previous comparisons obviously failed. In general, this means that the item we’re adding is actually greater than the values we were able to compare it to in the previous time through the loop. What are these other values the tests would have failed for in the previous time through our loop?
One hint relates to the what we keep saying about the availability of information from our current position in a one-way linked list; “Unless we do something special, we only know the current and the next item.” So, let’s start looking at the other test we’ve not yet include for “current” and “current.next”.
First, remember, if current.next is equal to NULL, we know we’re at the last item in our list. If current.next equals NULL and the new item’s value is equal to or greater than current’s, then we know the new item should be added to the end of the list. So let’s add this test to get this out of the way:
…
current = list.start;
compareItems = comparator;
current = list.start;
while( current != NULL )
{
// if we need to add the item to the end of the list…
if ( current.next == NULL && compareItems( item, current ) >= 0 )
{
current.next = item;
list.end = item;
return 1;
}
if ( compareItems( item, current ) < 0 ) {
// if we need to add item to the start of the list…
if ( previous == NULL )
{
item.next = start;
list.start = item;
return 1;
}
}
previous = current;
current = current.next;
}
…
Since he major part of our test is whether or not current.next is equal to NULL, I placed this ahead of all other conditions in the “if”. Next, rather than have two separate test for “equal to” and “greater than”, I simply use the “!” (not) operator to see if the comparison result is “not negative”; ( 0 or above). If it is, then we know we are adding to the end of the list. Current.next and the list’s end simply need to be set to our new item.
The test condition is another “odd ball” condition and we’ve gotten it out of our way. As we add items to the list, there are more cases where “current.next” won’t be NULL and we’ll be able to use it’s value in a comparison test. What do you think would be a good test to make in this situation?
I don’t know what you would test for, but to me, the obvious test would be to see if the new item should be inserted between “current” and “current.next”; the new item’s value is equal to or greater than currents and less than current.next’s. If you think about it, there will be more situations where we’ll want to try to insert our item between “current” and “current.next”!
…
current = list.start;
compareItems = comparator;
current = list.start;
while( current != NULL )
{
// if item is greater to or equal to current and less
// than current next…
if ( current.next != NULL )
{
if ( compareItems( item, current ) >= 0 &&
compareItems(item, current.next) < 0 )
{
item.next = current.next;
current.next = item;
return 1;
}
}
// if we need to add the item to the end of the list…
if ( current.next == NULL && compareItems( item, current ) >= 0 )
{
current.next = item;
list.end = item;
return 1;
}
if ( compareItems( item, current ) < 0 ) {
// if we need to add item to the start of the list…
if ( previous == NULL )
{
item.next = start;
list.start = item;
return 1;
}
}
previous = current;
current = current.next;
}
…
I think we’ve just about tackled our problem, but let’s see if we can clean things up a bit. We got a bit messy when we created our first tests to see if the new item should become the first item of the list. We created the sub-condition, “if previous == NULL”. We can now move this up into the “if” condition that contains this, because there will be no other times where compareItems() should return a value < 0. This is because of the ordering of our “if” statements:
compareItems = comparator;
current = list.start;
while( current != NULL )
{
// if item is greater to or equal to current and less
// than current next…
if ( current.next != NULL )
{
if ( compareItems( item, current ) >= 0 &&
compareItems(item, current.next) < 0 )
{
item.next = current.next;
current.next = item;
return 1;
}
}
// if we need to add the item to the end of the list…
if ( current.next == NULL && compareItems( item, current ) >= 0 )
{
current.next = item;
list.end = item;
return 1;
}
// If the item is less than the first item in the list…
if (previous == NULL && compareItems( item, current ) < 0 ) {
item.next = start;
list.start = item;
return 1;
}
previous = current;
current = current.next;
}
…
Let’s put all the code together in one place and walk through it:
int insertionSort( ITEM* item, LIST* list, void* comparitor)
{
ITEM* current;
ITEM* previous;
if ( item != NULL && list != NULL && comparator != NULL)
{
if ( list.start != NULL )
{
compareItems = comparator;
current = list.start;
while( current != NULL )
{
// if item is greater to or equal to current
//and less than current next…
if ( current.next != NULL )
{
If ( compareItems( item, current ) >= 0 &&
compareItems(item, current.next) < 0 )
{
item.next = current.next;
current.next = item;
return 1;
}
}
// if we need to add the item to the end
// of the list…
if ( current.next == NULL &&
compareItems( item, current ) >= 0 )
{
current.next = item;
list.end = item;
return 1;
}
// If the item is less than the first item in the list…
if (previous == NULL &&
compareItems( item, current ) < 0 ) {
item.next = start;
list.start = item;
return 1;
}
previous = current;
current = current.next;
}
} else {
list.start = item;
return 1;
}
}
return 0;
}
Let’s use our items containing A,B and C values to walk through our logic. Let’s say we start with the item that contains “B” first. At this point, “list.start” will equal NULL, so we fail this test, so go to the “else” for it and simply add the first item to the list.
Now, let’s say we attempt to add the item containing “A”. Now, “list.start” is no longer NULL, so we prepare to loop through items and compare the item we want to add to existing items. We have only 1 item in the list, so it’s the end, so it’s “next” is NULL, so we fail the first test immediately and we move on to the next. This next test finds that “current.next” is in fact NULL, but “A” is less than “B”, so this test also fails. Now, since this is the first time through our loop “previous” is equal to NULL. “A” is found to be less than “B”, so the test succeeds, so we push “A” into the start of the list and reconnect “B” to it.
Next, we are left to add our last item, “C”. Once again “list.start” is not equal to NULL, so this test succeeds, so we prepare to loop through the items in the list and make comparisons. When we hit the first test, “current.next” is not equal to NULL, “ C” greater than “A”, but “C” is not less than “B”, so this test fails. If you walk through the next 2 “if” tests, you’ll find that we will fail these, as well, so we get the next item and set “previous” to “current”. We are now at item “B” (the second item) in our list. Since “B” is the last item in our list, it’s “next” is NULL, so we fail the first test, and move on to the next. Here, we find that “current.next” is NULL and “C” is equal to or greater than “B”, so “C” is added to the end of the list.
The above scenario worked as planned. If we walk through each item in the list, “A” will be found to be the first item, followed by “B”, then the last item in the list, “C”. However, you’ll want to try different item combination to fully test our logic. For instance, start with “C”, then “A”, and then “B”. As another test, add “C” first, then “B”, followed by “A”. In another series of tests, you want to start with “A”, then add “B”, then “C”, then test again by adding “A”, but swapping the order in which you add “B” and “C”.
Here’s one more way of looking the logic we’ve come up with. If we have one item in the list, we will either add it to the end or make it the new beginning. Any time after that, the same will hold true, but we will have some sets of “current-current.next” to test to see if the new item should be placed between these.
Though we didn’t talk about this specifically, we’ve included logic to handle items that might be equal to any one of the items in our list. If we didn’t and this occurred, they would not be added and the insertionSort() function would return 0. However, you may not want duplicates in your list, as sometimes is the case. If you don’t, you would have to change tests to only include greater-than and less-than tests, so any attempt to add duplicates would fail. You could copy our existing function, rename it and then change the logic for the no-duplicates scenario; i.e., “insertionSortNoDups”.
Now, let’s add the calls to our insertionSort() to our main():
,,,
#ifdef NUMERIC
ITEM* one = {number = 1; next = NULL;}
ITEM* two = {number = 2; next = NULL;}
ITEM* three = {number = 3; next = NULL;}
insertionSort(list, two, compareNumeric);
insertionSort(list, three, compareNumeric);
insertionSort(list, one, compareNumeric);
#endif
#ifdef TEXT
ITEM* A = NULL;
ITEM* B = NULL;
ITEM* C = NULL;
A = malloc( sizeof(ITEM));
strcpy(A.letter, "A");
A.next = NULL;
B = malloc( sizeof(ITEM));
strcpy(B.letter, "B");
B.next = NULL;
C = malloc( sizeof(ITEM));
strcpy(C.letter, "C");
C.next = NULL;
insertionSort(B, list, compareText);
insertionSort(C, list, compareText);
insertionSort(A, list, compareText);
#endif
Seeing Is Believing
Well, we’ve got the bulk of our coding completed. However, we don’t have a way to look at our list. Let’s create an additional piece of code to walk through the list and print our list items:
void traverseOneWayList(LIST* list, void* handler)
{
ITEM* cur = list.start;
procOneWayTraverseHandler = handler;
while ( cur != NULL )
{
procOneWayTraverseHandler(cur);
cur = cur->next;
}
}
This function is taken from the first tutorial. Just like our insertionSort(), one of the parameters (handler) is meant to be a pointer to a function. The only thing this function must do is to display the value stored in the list item. Let’s create our global pointer, as shown being used within this function:
void procOneWayTraverseHandler(ITEM* cur);
Next, we need a couple functions to display the item data; one for numeric and one for text:
#ifdef NUMERIC
,,,
void displayNumeric(ITEM* item)
{
printf("%d",item.number);
}
#endif
#ifdef TEXT
void displayText(ITEM* item)
{
printf("%s",item.letter);
}
#endif
Pretty simple stuff… except for printf()… what does it do? It displays a little dialog box containing the message you ask it display. The first parameter of printf() is a format string, which helps it determine any conversions it needs to make to display your message as text. I know it is possible in simple situations like the one above to simply drop the format string parameter and just pass in a string constant (i.e., “Hello World”) or the variable containing the value you want it to display. If you run into a problem with this method when attempting to display a numeric, then keep the format parameter, using “%d”, meaning “decimal”.
Also, notice that each function will need to be placed within an appropriate #ifdef, since each of ITEM structs are different. If you don’t, you will receive a compiler error saying that item.letter or item.number is not a member of the struct.
With this test code work being complete, let’s use it to see the results of our list builder of both types of lists. The contents of main() should look like this:
#ifdef NUMERIC
ITEM* one = {number = 1; next = NULL;}
ITEM* two = {number = 2; next = NULL;}
ITEM* three = {number = 3; next = NULL;}
insertionSort(mylist, two, compareNumeric);
insertionSort(mylist, three, compareNumeric);
insertionSort(mylist, one, compareNumeric);
traverseOneWayList(list, displayNumeric);
#endif
#ifdef TEXT
ITEM* A = NULL;
ITEM* B = NULL;
ITEM* C = NULL;
A = malloc( sizeof(ITEM));
strcpy(A.letter, "A");
A.next = NULL;
B = malloc( sizeof(ITEM));
strcpy(B.letter, "B");
B.next = NULL;
C = malloc( sizeof(ITEM));
strcpy(C.letter, "C");
C.next = NULL;
insertionSort(B, list, compareText);
insertionSort(C, list, compareText);
insertionSort(A, list, compareText);
traverseOneWayList(list, displayText);
free(A);
free(B);
free(C);
#endif
Conclusion
In the long haul, we accomplished a sort. There are other ways to sort items, but in most cases, an insertion sort is simple and works well when you want to sort a linked list. As the name implies, an insertion sort sorts the items as they are added to a list.
We also threw in a few other techniques during our demonstration of the insertion sort. We demonstrated how we can use #define and #ifdef-#endif to conditionally run parts of our code for testing. We also showed how giving our structs generic names line LIST and ITEM help the code to become more re-useable. Once again, we used function pointers to specify which comparison function we wanted to use during item insertion, as well as for printing item information while the list items are being visited. Though you don’t want to go wild with function pointers, there are choice situations where they make sense and are very useful!
A full code listing follows. Play with the code as you wish and explore your own ideas! I hope this was a helpful tutorial.
LINKED_SORT.C LISTING
#include <acknex.h>
#include <default.c>
// CHANGE THIS FROM "TEXT" TO "NUMERIC" TO CHANGE BETWEEN
// LIST ITEM DATA TYPES.
#define TEXT
// ----------------------------------------------------
char* strcpy(char* a, char* b);
int strcmp(char* a, char* b);
strcpy = DefineApi("mscvrt!strcpy");
strcmp = DefineApi("mscvrt!strcmp");
#ifdef TEXT
typedef struct listItem
{
char letter[2];
struct listItem* next;
} ITEM;
#endif
#ifdef NUMERIC
typedef struct listItem
{
int number;
struct listItem* next;
} ITEM;
#endif
// Comparison function pointer
int compareItems(ITEM* in, ITEM* current);
void procOneWayTraverseHandler(ITEM* cur);
typedef struct list
{
ITEM* start;
ITEM* end;
} LIST;
#ifdef NUMERIC
int compareNumeric(ITEM* in, ITEM* current)
{
return in.number - current.number;
}
void displayNumeric(ITEM* item)
{
printf("%d",item.number);
}
#endif
#ifdef TEXT
int compareText(ITEM* in, ITEM* current)
{
return strcmp(in.letter, current.letter);
}
void displayText(ITEM* item)
{
printf("%s",item.letter);
}
#endif
int insertionSort( ITEM* item, LIST* list, void* comparator)
{
ITEM* current;
ITEM* previous;
if ( item != NULL && list != NULL && comparator != NULL)
{
if ( list.start != NULL )
{
compareItems = comparator;
previous = NULL;
current = list.start;
while( current != NULL )
{
// if item is greater to or equal to current and
// less than current next
if ( current.next != NULL )
{
if ( compareItems( item, current ) >= 0 &&
compareItems(item, current.next) < 0 )
{
item.next = current.next;
current.next = item;
return 1;
}
}
// if we need to add the item to the end of the list
if ( current.next == NULL &&
compareItems( item, current ) >= 0 )
{
current.next = item;
list.end = item;
return 1;
}
// If the item is less than the first item
// in the list
if (previous == NULL &&
compareItems( item, current ) < 0 )
{
item.next = list.start;
list.start = item;
return 1;
}
previous = current;
current = current.next;
}
} else {
list.start = item;
list.end = item;
return 1;
}
}
return 0;
}
void traverseOneWayList(LIST* list, void* handler)
{
ITEM* cur = list.start;
procOneWayTraverseHandler = handler;
while ( cur != NULL )
{
procOneWayTraverseHandler(cur);
cur = cur->next;
}
}
LIST* list = {start = NULL; end = NULL;}
void main()
{
#ifdef NUMERIC
ITEM* one = {number = 1; next = NULL;}
ITEM* two = {number = 2; next = NULL;}
ITEM* three = {number = 3; next = NULL;}
insertionSort(two, list, compareNumeric);
insertionSort(one, list, compareNumeric);
insertionSort(three, list, compareNumeric);
traverseOneWayList(list, displayNumeric);
#endif
#ifdef TEXT
ITEM* A = NULL;
ITEM* B = NULL;
ITEM* C = NULL;
A = malloc( sizeof(ITEM));
strcpy(A.letter, "A");
A.next = NULL;
B = malloc( sizeof(ITEM));
strcpy(B.letter, "B");
B.next = NULL;
C = malloc( sizeof(ITEM));
strcpy(C.letter, "C");
C.next = NULL;
insertionSort(B, list, compareText);
insertionSort(C, list, compareText);
insertionSort(A, list, compareText);
traverseOneWayList(list, displayText);
free(A);
free(B);
free(C);
#endif
}