In the previous two posts (here and here), we have introduced linked list data structure and discussed various types of linked lists. We also covered in great detail the various methods to construct a linked list that inserts every new node onto the list’s front. This post will discuss various methods to implement a linked list by inserting it at the tail of the singly linked list.

Practice this problem

 
A simple solution would be to locate the last node in the list and then change its .next field from NULL to point the new node. This is just a particular case of the general rule: to insert or delete a node inside a list, we need a pointer to the node just before that position to change its .next field. Many list problems include the subproblem of advancing a pointer to the node before the point of insertion or deletion. The one exception is if the node is first in the list – in that case, we must change the head pointer.

 
Consider below the appendNode() function, which is like push(), except it adds the new node at the tail end of the list instead of the head. If the list is empty, it uses the reference pointer to change the head pointer. Otherwise, it uses a loop to locate the last node in the list. This version does not use push(), but builds the new node directly.

Following is the C, Java, and Python program that demonstrates it:

C


Download  Run Code

Output:

1 —> 2 —> 3 —> 4 —> NULL

Java


Download  Run Code

Output:

1 —> 2 —> 3 —> 4 —> null

Python


Download  Run Code

Output:

1 —> 2 —> 3 —> 4 —> None

The following version is very similar to the above code but relies on push() to build the new node. Understanding this version requires a real understanding of reference pointers.

C


Download  Run Code

Output:

1 —> 2 —> 3 —> 4 —> NULL

Java


Download  Run Code

Output:

1 —> 2 —> 3 —> 4 —> null

Python


Download  Run Code

Output:

1 —> 2 —> 3 —> 4 —> None

The time complexity in the above solution would be linear for each insertion as we are traversing the whole list till the very end. An efficient approach is maintaining a tail pointer and a head pointer to perform insertion in constant time. There are two standard ways to do it:

1. Build using Dummy Node

The idea is to use a temporary dummy node at the head of the list during computation. The trick is that every node appears to be added after the .next field of a node with the dummy. That way, the code for the first node is the same as for the other nodes. The tail pointer plays the same role as in the previous example. It now also handles the first node (avoids making dummy a permanent part of the list).

Following is the C, Java, and Python program that demonstrates it:

C


Download  Run Code

Output:

1 —> 2 —> 3 —> 4 —> NULL

Java


Download  Run Code

Output:

1 —> 2 —> 3 —> 4 —> None

Python


Download  Run Code

Output:

1 —> 2 —> 3 —> 4 —> None

Some linked list implementations keep the dummy node as a permanent part of the list. For this “permanent dummy” strategy, the empty list is not represented by a NULL pointer. Instead, every list has a dummy node at its head. The algorithms skip over the dummy node for all operations. The heap-allocated dummy node is always present to provide the above sort of convenience in the code.

2. Build using Local References

A tricky way to unifying all the node cases without using a dummy node is to use a local “reference pointer”, which always points to the last pointer in the list instead of the last node. All additions to the list are made by following the reference pointer. The reference pointer starts by pointing to the head pointer. Later, it points to the .next field inside the last node in the list.

The implementation can be seen below in C:

C


Download  Run Code

Source: http://cslibrary.stanford.edu/103/LinkedListBasics.pdf