CS-211 Fall 2015 Lab 8 Data Structures
In the last couple of lectures, we covered the idea of structures, structures which contain fields that point to other instances of the same structure, and dynamic allocation. Once example that uses all three concepts is a linked list; a data structure which consists of a list of nodes, where every node contains a payload (one or more values) and a pointer to a next node. The entire list starts with a pointer to the first node, called the "head" of the list. The first node's next pointer then points to the second node, whose next pointer points to the third node, and so on, until you get to a node whose next pointer is zero or NULL. This last node is the tail of the list.
We can use dynamic allocation (malloc) to create a new instance of a node, and that node can be inserted anywhere in the list by manipulating the "next" pointers.
For this lab, we will be creating a linked list. The payload part of each node in that list will consist of two integers - the first is the "value" of the node, just some arbitrary number; and the second is a "count", which counts the number of occurences of the value. We are also going to impose a new rule on our linked list... namely that the value in any node in the list will be less than the value in the next node in the list. In other words, the first node in the list will have the lowest value, the second will be greater than the first, the third greater than the second, and so on, until you get to the tail which has the greatest value of all.
Our linked list will be created from an arbitrary, unsorted list of integers read in from standard input.
After we have created our list, we will print out the values and counts for each node in that list.
Download the code that has been started for you in lab8.tar.gz. Use the command tar -xvzf lab8.tar.gz. This will unpack the tar file and make a new lab8 subdirectory. The lab8 directory will have a Makefile, and the C code to start from in file llist.c
The llist.c File
The llist.c file starts of with the standard includes for standard library functions, a definition of the lnode structure, and prototypes for the five lower level functions in the file.
What follows is just the normal function definitions that we are used to seeing. I've included a description of each of these functions below.
- int main() The main function performs the following actions:
You don't have to change the main function for this lab.
- Declares a head variable to point to the first node in our linked list. Since our linked list starts out empty, head is initialized to NULL.
- Declares a nextVal integer variable to hold the next value read from standard input
- Invokes the scanf standard library function, which reads then next number from standard input, and puts the value of that number in the nextVal variable. If scanf doesn't find a number on standard input, it returns a zero value. For each value that is read, the main function performs the following actions...
- Invokes the insertValue function, passing in as arguments the value read from standard input, and a pointer to the head of our linked list. The insertValue function should account for the new value by adding it to the list, or updating the count for an item already in the list. In order to do this, the insertValue function may keep the same head of the list, or may create a new head of the list. In either case, we expect insertValue to return the head of the updated list.
- Checks to make sure that the resulting linked list is still in sort order. (As long as insertValue works correctly, our list should always be sorted... so this check just makes sure isertValue is working correctly.) It makes this check by invoking the orderedLlist function, and asserting that the result of that function should always be true.
- When main is finished reading all the input values, it prints a message that says "Finished building our linked list"
- Then main invoked the printLlist function to print out the results our our linked list.
- Finally, now that main is finished with our linked list, it invokes the freeLlist function to free all the nodes we have accumulated in our linked list.
- struct lnode * insertValue(int val,struct lnode *head) The insertValue function is the function you need to implement for this lab. There is more information about how to implement the insertValue function below.
The remaining functions have already been coded for you. You do not need to modify these functios.
- struct lnode * makeLnode(int val) The makeLnode function dynamically allocates the memory required for a new instance of the lnode structure, and initializes the fields in that structure. It initializes the value field to whatever was passed in as the "val" argument. It initializes the count field to one (assuming this is the first istance of this value we have seen), and it initializes the next field to NULL (assuming that we will hook up the next field outside of this function.) The makeLnode function finally returns a pointer to this new instance.
The last three functions work on an entire linked list.
- void freeLlist(struct lnode *ln) This function takes the pointer to the head of a linked list as the ln argument. As long as ln is not null, the function saves the next pointer from ln, frees ln, and then resets ln to the next pointer. In effect, this frees the memory associated with the head of the list, and resets the head to the next node. Eventually, we free the node associated with the tail, and then stop.
- void printLlist(struct lnode *ln) The printLlist function takes the pointer to the head of hte linked list as the ln argument. The function prints information about the ln node, and then if there is a next node, updates ln to point to that next node. This happens in a loop until we get to the tail node.
- int orderedLlist(struct lnode *ln) This function checks to make sure that the linked list whose head is the ln argument is in order. If the list is not in order, this function returns a zero (false). If the list is in order, it returns a 1.
Using The Makefile
The Makefile file in the lab8 directory contains "targets" which allow you to do several things using the make command. The following targets are supported:
- make llist - Makes the llist executable if you have changed the source code.
- make test - Makes the llist executable if you have changed the source code, and then runs that executable using redirection to feed the contents of the input.txt file into standard input.
- make clean - Removes the llist executable.
Inserting a Node into an Ordered Linked List
If you thought about it for awhile, you could probably figure out exactly how to insert a node into an ordered linked list without any help, but in order to save time, here is a description of exactly how to insert a node. In this description, we will assume that you have invoked insertValue, passing in the val value to insert, and head points to the ordered linked list that we want to insert the value into.
- First, we need to consider a special case... if our list starts out empty (i.e. empty when head==NULL), we can just make a new node with the input value provided to us. This new node will then become the head of our new linked list. If we pass val to the makeLnode function, that function will intialize a new node just like we want, with a value of val, a count of 1, and a next field of NULL (to indicate that this will be the tail of the list.) All we need to do is to return a pointer to this new node as our new linked list head.
- Next, we need to consider one other special case. If val is less than the value of the head node, then we need to insert a new node at the head of the list. We do this by invoking makeLnode, passing in val as an argument. The makeLnode function will return a pointer to the new node. We then need to set the next pointer in that new node to our old head, and return the address of the new node as our new head pointer.
- In all other cases, we can assume that head is not NULL, and that our new val will not go in front of the head pointer. We need to walk through our list until either the value of a node matches val, or the value of the next node is greater than val, which means we need to make a new node from val and insert our new node between whatever we are at, and the next node. If we get all the way to the end of the list, and haven't met either condition, we simply put val in a new node at the end of the list.
We will walk through our linked list using a loop. We need a variable to point to the current entry of the list, so we will need to declare a variable such as current to be a pointer to a linked list node. We will start current and the head of the list.
As long as current is not NULL, we can execute the body of the loop. In the body of the loop, we will do the following...
- If current->value matches val, then all we need to do is increment current->count and return our original head pointer.
- If current->next is NULL, then current is the tail of the list. Therefore, we need to invoke makeLnode, passing in val as an argument, set current->next to the value returned by makeLnode, and return our original head pointer.
- If current->next->value is greater than val, then we need to insert a new node after current and before current->next. We do this by invoking makeLnode passing in val, setting newNode->next=current->next, and setting current->next=newNode (as in Lecture 22, page 9). We then return our original head pointer
- If none of the above conditions are true, we need to move to the next item in our linked list. To do this, we will increment our current pointer by setting it to the next pointer of the node pointed to by current...current=current->next;
Checking for Memory Leaks
After you have written and tested your implentation of the insertValue function, test to see if your llist command has any memory leaks. To do this, we will use the valgrind command. To run valgrind, execute the following command:
valgrind --leak-check=full ./llist <input.txt
If you have implemented insertValue correctly, you should get no memory leaks, and see the message at the end of the valgrind invocation that includes:
All heap blocks were freed -- no leaks are possible
Download and edit the following file: lab8_report.txt. Then submit your editted file on Blackboard in the Lab_08 report area.