Earlier we discussed the methods of linked list. These methods form the interface of the link list. For further elucidation of these techniques, we will talk about the start method that has the following code.
// position currentNode and lastCurrentNode at first element
void start() {
lastCurrentNode = headNode;
currentNode = headNode;
};
|
There are two statements in this method. We assign the value of headNode to both lastCurrentNode and currentNode. These two pointers point at different nodes of the list. Here we have pointed both of these pointers at the start of the list. On calling some other method like next, these pointers will move forward. As we can move in the singly-linked list in one direction, these pointers cannot go behind headNode.
We will now see how a node can be removed from the link list. We use the method remove for this purpose.
void remove() {
if( currentNode != NULL && currentNode != headNode) {
(step 1) lastCurrentNode->setNext(currentNode->getNext());
(step 2) delete currentNode;
(step 3) currentNode = lastCurrentNode->getNext();
(step 4) size--;
}
};
|
Suppose that the currentNode is pointing at the location that contains the value 6. A request for the removal of the node is made. Resultantly, the node pointed by currentNode should be removed. For this purpose, at first, the next pointer of the node with value 2 (the node pointed by the lastCurrentNode pointer), that is before the node with value 6, bypasses the node with value 6. It is, now pointing to the node with value 8. The code of the first step is as:
lastCurrentNode->setNext(currentNode->getNext());
What does the statement currentNode->getNext() do? The currentNode is pointing to the node with value 6 while the next of this node is pointing to the node with value 8. That is the next pointer of node with value 6 contains the address of the node with value 8. The statement lastCurrentNode->setNext(currentNode->getNext()) will set the next pointer of the node pointed by the lastCurrentNode to the node with value 8. So the next pointer of the node with value 2 is pointing to the node with value 8.
You see that the next pointer of the node having data element 2 contains the address of the node having data element 8. The node with value 6 has been disconnected from the chain while the node with value 2 is connected to the node with the value 8.
The code of the next step is:
delete currentNode;
You already know, in case of allocation of the memory with the help of the new keyword, the delete statement releases this memory which returns the memory to the system. Pictorially it can be represented as:
In the next step, we have moved the currentNode to point the next node. The code is:
currentNode = lastCurrentNode->getNext();
In the fourth step, the size of the list has been reduced by 1 after the deletion of one node i.e.
size--;
The next method is length() that simply returns the size of the list. The code is as follows:
// returns the size of the list
int length()
{
return size;
};
The private data members of the list are:
private:
int size; // contains the size of the list
Node *headNode; // points to the first node of the list
Node *currentNode, // current node
Node *lastCurrentNode; // last current node
|
The list class completed just now, can be termed as list factory. We have included all the required methods in it. We may employ more methods if required. A programmer can get the size of the list, add or remove nodes in it besides moving the pointers.
Example of list usage
Now let’s see how we use the link list. Here is an example showing the use of list:
/* A simple example showing the use of link list */
#include <iostream>
#include <stdlib.h>
#include "List.cpp" // This contains the definition of List class
// main method
int main(int argc, char *argv[])
{
List list; // creating a list object
// adding values to the list
list.add(5);
list.add(13);
list.add(4);
list.add(8);
list.add(24);
list.add(48);
list.add(12);
// calling the start method of the list
list.start();
// printing all the elements of the list
while (list.next())
cout << "List Element: "<< list.get()<<endl;
}
|
The output of the program is:
List Element: 5
List Element: 13
List Element: 4
List Element: 8
List Element: 24
List Element: 48
List Element: 12
|
Let’s discuss the code of the above program. We have included the standard libraries besides having the “List.cpp” file. Usually we do not include .cpp files. Rather, the .h files are included. Whenever you write a class, two files will be created i.e. .h (header file containing the interface of the class) and .cpp (implementation file). Here for the sake of explanation, we have combined the two files into “List.cpp” file. At the start of the main method, we have created a list object as:
List list;
Here the default constructor will be called. If you understand the concept of factory, then it is not difficult to know that we have asked the List factory to create a List object and named it as list. After creating the object, nodes have been added to it. We have added the elements with data values 5, 13, 4, 8, 24, 48 and 12. Later, the start() method of list is called that will position the currentNode and lastCurrentNode at the start of the list. Now there is no need to worry about the implementation of the List. Rather, we will use the interface of the List. So the start method will take us to the start of the list and internally, it may be array or link list or some other implementation. Then there is a while loop that calls the next() method of the List. It moves the pointer ahead and returns a boolean value i.e. true or false. When we reach at the end of the list, the next() method will return false. In the while loop we have a cout statement that prints the value of the list elements, employing the get() method. The loop will continue till the next() method returns true. When the pointers reach at the end of the list the next() will return false. Here the loop will come to an end.
Please keep in mind that list is a very important data structure that will be used in the entire programming courses.
Analysis of Link List
As stated earlier, we will be going to analyze each data structure. We will see whether it is useful or not. We will see its cost and benefit with respect to time and memory. Let us analyze the link list which we have created with the dynamic memory allocation in a chain form.
Add
For the addition purposes, we simply insert the new node after the current node. So ‘add’ is a one-step operation. We insert a new node after the current node in the chain. For this, we have to change two or three pointers while changing the values of some pointer variables. However, there is no need of traversing too much in the list. In case of an array, if we have to add an element in the centre of the array, the space for it is created at first. For this, all the elements that are after the current pointer in the array, should be shifted one place to the right. Suppose if we have to insert the element in the start of the array, all the elements to the right one spot are shifted. However, for the link list, it is not something relevant. In link lists, we can create a new node very easily where the current pointer is pointing. We have to adjust two or three pointers. Its cost, in terms of CPU time or computing time, is not much as compared to the one with the use of arrays.
Remove
Remove is also a one-step operation. The node before and after the node to be removed is connected to each other. Update the current pointer. Then the node to be removed is deleted. As a result, the node to be removed is deleted. Very little work is needed in this case. If you compare it with arrays, for the deletion of an element from the array, space is created. To fill this space, all the right elements are shifted one spot left. If the array size is two thousand or three thousand, we need to run a loop for all these elements to shift them to left.
Find
The worst-case in find is that we may have to search the entire list. In find, we have to search some particular element say x. If found, the currentNode pointer is moved at that node. As there is no order in the list, we have to start search from the beginning of the list. We have to check the value of each node and compare it with x (value to be searched). If found, it returns true and points the currentNode pointer at that node otherwise return false. Suppose that x is not in the list, in this case, we have to search the list from start to end and return false. This is the worst case scenario. Though time gets wasted, yet we find the answer that x is not in the list. If we compare this with array, it will be the same. We don’t know whether x is in the array or not. So we have to search the complete array. In case of finding it, we will remember that position and will return true. What is the average case? x can be found at the first position , in the middle or at the end of the list. So on average, we have to search half of the list.
Back
In the back method, we move the current pointer one position back. Moving the current pointer back, one requires traversing the list from the start until the node whose next pointer points to current node. Our link list is singly linked list i.e. we can move in one direction from start towards end. Suppose our currentNode pointer and lastCurrentNode are somewhere in the middle of the list. Now we want to move one node back. If we have the pointer of lastCurrentNode, it will be easy. We will assign the value of lastCurrentNode to currentNode. But how can we move the lastCurrentNode one step back. We don’t have the pointer of previous node. So the solution for this is to go at the start of the list and traverse the list till the time you reach the node before the lastCurrentNode is pointing. That will be the node whose next pointer contains the value lastCurrentNode. If the currentNode and the lastCurrentNode are at the end of the list, we have to traverse the whole list. Therefore back operation is not a one step operation. We not only need a loop here but also require time.