Data structures: Linked List
What is a linked list?
A linked list is a widely used data structure in which, as the name suggests, the items are stored in a not contigous way. We can think about a linked list as a chain of what we are going to call nodes. Essentially the function of the node is quite simple: store a value or key and the address of the next node.
Difference between arrays and linked lists
- Arrays store elements in contigous way. Thus the access to an element at given index is faster than in a linked list.
- Linked lists store elements in a not contigous way.
- Linked lists are dynamic in size. Arrays have a fixed size that cannot be modified at runtime.
- Insertion in a linked list is faster than in a array
- Accessing elements is faster in an array than in a linked list. In order to access an element in a linked list you have to traverse it.
Building a linked list in C++
First thing first we are going to implement the Node class. Since we are not interested in any particular data type we might use templates.
template <typename T>
class Node
{
public:
Node(T value);
T data;
Node<T>* next;
};
Then we proceed to implement our LinkedList class
template<typename T>
class LinkedList
{
public:
// Constructor
LinkedList();
Node<T>* head;
Node<T>* tail;
// Methods
Node<T>* GetData(int index);
void InsertHead(T val);
void InsertTail(T val);
void Insert(int index, T val);
void RemoveHead();
void RemoveTail();
void Remove(int index);
void PrintList();
int CountElements();
private:
int count_elements;
};
Now can we implement the class methods:
- GetData: return the value associated with the given index.
- InsertHead: replace the first node in the linked list with the given one.
- InsertTail: replace the last node in the linked list with the given one.
- Insert: Insert a node at given index.
- RemoveHead: Remove head node.
- RemoveTail: Remove tail node.
- Remove: Remove node at given index.
- PrintList: print the list.
- CountElements: return the number of elements.
Get the node at given index
template<typename T>
Node<T>* LinkedList<T>::GetData(int index)
{
// Check if index is out of bound
if(index < 0 || index > count_elements)
{
std::cout << "Invalid index";
return nullptr;
}
// Start from head
Node<T>* node = head;
// Traverse the list to the selected index and return it
for(int i = 0; i < index; ++i)
{
node = node->next;
}
return node;
}
Insertion at the beginning of the list
template<typename T>
void LinkedList<T>::InsertHead(T val)
{
// Create a new node
Node<T>* node = new Node<T>(val);
// Check if list is empty. If it is, head is equal to tail
if(count_elements == 0)
{
tail = head;
}
// the new node becomes the head
node->next = head;
node = head;
}
Insertion at the end of the list
template <typename T>
void LinkedList<T>::InsertTail(T val)
{
// If the linked list is empty, tail is equal to head
if(count_elements == 0)
{
InsertHead(val);
return;
}
// Create a new node
Node<T> * node = new Node<T>(val);
// The current Tail will no longer become a Tail
// so the Next pointer of the current Tail will
// point to the new node
tail->next = node;
// The new node now becomes the tail
tail = node;
count_elements++;
}
Insert a node at a specific index
template<typename T>
void LinkedList<T>::Insert(int index, T val)
{
if(index < 0 || index > count_elements)
{
std::cout << "Invalid Index";
return;
}
// Insert a new head
if(index == 0)
{
InsertHead(val);
return;
}
// Inserting a new tail
if(index == count_elements)
{
InsertTail(val);
return;
}
// Start to find previous node from the Head
Node<T>* prevNode = Head;
// Traverse the elements until the selected index is found
for (int i = 0; i < index - 1; i++)
{
prevNode = prevNode->next;
}
// Create the next node which is the element after previous node
Node<T>* nextNode = prevNode->next;
// Create a new node
Node<T>* node = new Node<T>(val);
// Insert this new node between the previous node and the next node
node->next = nextNode;
prevNode->next = node;
count_elements++;
}
Remove head from linked list
template <typename T>
void LinkedList<T>::RemoveHead()
{
if(count_elements == 0)
{
std::cout << "No element found\n";
return;
}
// Save the current Head to a new node
Node<T>* node = head;
// Point the Head pointer to the element
// next to the current head(the second element)
Head = head->next;
// Now it is safe to remove the first element
delete node;
count_elements--;
}
Remove tail from linked list
template <typename T>
void LinkedList<T>::RemoveTail()
{
if(count_elements == 0)
{
std::cout << "No element found\n";
return;
}
if(count_elements == 1)
{
RemoveHead();
return;
}
// Save the current head to a new node
Node<T>* prevNode = head;
// Node that will be removed
Node<T>* node = head->next;
// Traverse the list until the last element
while(node->next != nullptr)
{
prevNode = prevNode->next;
node = node->next;
}
// Delete the node
delete node;
count_elements--;
}
Remove element at given index
template <typename T>
void LinkedList<T>::Remove(int index)
{
if(count_elements == 0)
{
std::cout << "No element found\n";
return;
}
if(index == 0)
{
RemoveHead();
return;
}
else if(index == count_elements -1)
{
RemoveTail();
return;
}
Node<T>* prevNode = head;
// Finding the element before given index
for(int i = 0; i < index - 1; i++)
{
prevNode = prevNode->next;
}
// node to be removed
Node<T>* node = prevNode->next;
// Node after prevNode
Node<T>* nextNode = node->next;
prevNode->next = nextNode;
delete node;
count_elements--;
}
Print the element inside the linked list
template <typename T>
void LinkedList<T>::PrintList()
{
if(count_elements == 0)
{
std::cout << "No element inside the list";
}
Node<T>* node = head;
// Traverse the list and print the data
while(node->next != nullptr)
{
std::cout << node->data << " ";
}
std::cout << std::endl;
}
Count the elements inside a linked list
template <typename T>
int LinkedList<T>::CountElements()
{
if(count_elements == 0)
{
std::cout << "List is empty";
}
return count_elements;
}
Conclusion
Linked lists are basic knowledge for any CS student. Don’t forget to add them in your toolbox.