Singly linked lists are a fundamental data structure in computer science, offering dynamic flexibility and memory efficiency. In Java, understanding and mastering them is key to unlocking efficient algorithms and problem-solving skills. This blog aims to guide you from basic concepts to advanced techniques, transforming you from a linked list novice to a ninja!
Anatomy of a Singly Linked List
Each node in a singly linked list comprises two parts: data and a reference to the next node. This pointer-based structure enables dynamic growth and memory management. The first node is called the head, and the last node has a null
reference as its next node.
In a singly linked list, each node points to the next node in the sequence, forming a linear chain. The last node typically points to a null reference, indicating the end of the list.
How to represent a Linked List in Java ?
A linked list is a fundamental data structure employed for the storage of a series of elements, objects, or nodes, characterized by the following attributes:
- Sequential arrangement of nodes.
- Each node comprises data along with a pointer to the subsequent node.
- The initial node serves as the head node.
- The final node contains data and points to null.
| data | next | ===> | data | next | ===> | data | next | ===> null
Visualizing Nodes: Unraveling the Building Blocks
In the intricate world of linked lists, nodes take center stage, acting as the fundamental units that store data and guide traversals. Let’s dissect the visual representation you provided:
Node Structure:
| data | next |===>
- data: This field holds the actual value, serving as the heart of the information a node carries. Its contents can vary depending on the application, from simple integers to complex objects.
- next: This crucial pointer acts as the bridge to the next node in the sequence. By following these references, we can navigate the entire linked list, one node at a time. The arrow
----->
represents this connection, visually depicting the flow from one node to the next.
Linked List Illustration
head ====> |10|===>|8|===>|9|===>|11|===> null
- head: The special node marking the beginning of the linked list. Think of it as the entrance gate, providing the entry point for accessing the list’s elements.
- Nodes: Each rectangular box, labeled with
|data|====>|next|
, represents a node in the chain. The values inside thedata
field indicate the stored values (10, 8, 9, 11). - null: This unique value, often denoted by
null
, signifies the end of the list. Just like reaching the end of a road, encounteringnull
indicates there are no more nodes to follow.
public class SinglyLinkedList {
private ListNode head; // head node to hold the list
// It contains a static inner class ListNode
private static class ListNode {
private int data;
private ListNode next;
public ListNode(int data) {
this.data = data;
this.next = null;
}
}
public static void main(String[] args) {
}
}
This Java code defines a class named SinglyLinkedList
with a private member variable head
, which represents the starting node of the linked list. Inside this class, there is a static inner class named ListNode
, which encapsulates the data and reference to the next node. The constructor of ListNode
initializes the data and sets the next node reference to null.
The main method serves as the entry point, although it’s currently devoid of content. We will proceed to furnish it with creation code shortly.
How to create a singly linked list in java ?
head ---> 10 ---> 8 ---> 1 ---> 11 ---> null
Now, let’s see how we created it:
- Initially, the head node points to null because the linked list is empty, i.e., head —> null.
- Let’s create the first node with data 10, which also points to null, i.e., 10 —> null.
- To hold nodes one by one, the head points to the node with 10. At this stage, the our list contains one node, i.e., head —> 10 —>.
- Let’s create the second node with data 8 and next pointing to null, i.e., 8 —> null.
- Connect the head node to the second node so that the list will contain 2 nodes, i.e., head –> 10 –> 8 –> null.
- Create the third node with data 1 and next pointing to null, i.e., 1 –> null.
- Here, we connect the third node to the second node so that the list contains 3 nodes, i.e., head –> 10 –> 8 –> 1 –> null.
- Finally, create the fourth node with data 11 and next pointing to null, i.e., 11 –> null.
- Connect this fourth node with the third node. Now, the linked list size is 4, i.e., head –> 10 –> 8 –> 1 –> 11 –> null.
Lets write code in above SinglyLinkedList empty main method.
// Let's create a linked list demonstrated in the comment below
// 10 --> 8 --> 1 --> 11 --> null
// 10 as head node of linked list
ListNode head = new ListNode(10);
ListNode second = new ListNode(8);
ListNode third = new ListNode(1);
ListNode fourth = new ListNode(11);
// Attach them together to form a list
head.next = second; // 10 --> 8
second.next = third; // 10 --> 8 --> 1
third.next = fourth; // 10 --> 8 --> 1 --> 11 --> null
This code snippet demonstrates the creation of a linked list with four nodes, where each node contains an integer value. The comments above describe the structure of the linked list being created, and the subsequent lines of code link the nodes together to form the desired sequence.
Final code look like:
public class SinglyLinkedList {
private ListNode head; // head node to hold the list
// It contains a static inner class ListNode
private static class ListNode {
private int data;
private ListNode next;
public ListNode(int data) {
this.data = data;
this.next = null;
}
}
public static void main(String[] args) {
// 10 --> 8 --> 1 --> 11 --> null
// 10 as head node of linked list
ListNode head = new ListNode(10);
ListNode second = new ListNode(8);
ListNode third = new ListNode(1);
ListNode fourth = new ListNode(11);
// Attach them together to form a list
head.next = second; // 10 --> 8
second.next = third; // 10 --> 8 --> 1
third.next = fourth; // 10 --> 8 --> 1 --> 11 --> null
}
}
How to print elements of Singly Linked List in java
As we know, to print all elements in a linked list, we need to traverse through all nodes in the linked list until the end of the linked list.
So,
head –> 10 –> 8 –> 1 –> 11 –> null
Algorithm & Execution
current = head;
while(current != null)
{
Sop(current.data);
current = current.next;
}
- Let’s create a current node which initially points to null, i.e., current –> null. At this point, the output is null.
- Now we will assign the head node of the linked list to this current node, i.e., current = head.
- To traverse the list node by node until the end of the list, we will use a while loop which checks whether the current node is null. If it is null, that means we reached the end of the list.
- So, for every iteration, we will print that particular node’s data and also shift the current node pointer to the next node so that we traverse to the next node.
Code
// Given a ListNode, prints all elements it hold
public void display(ListNode head)
{
if(head == null)
{
return;
}
ListNode current = head;
// loop each element till end of the list
// last node points to null
while(current != null)
{
System.out.println(current.data + " --> "); // prints current element's data
// move to next element
current = current.next;
}
System.out.print(current); // here current wil be null
Run with -->
SinglyLinkedList singlyLinkedList = new SinglyLinkedList();
singlyLinkedList.display(head);
o/p : 10 --> 8 --> 1 --> 11 --> null
This code snippet defines a method display
within the SinglyLinkedList
class, responsible for printing all elements of the linked list starting from the provided head node. It iterates through the linked list nodes, printing each node’s data, and ends with printing “null” once the end of the list is reached. Finally, the method is invoked with a head node, displaying the elements of the linked list.
How to find the length of singly linked list in java?
Let’s determine the length of the linked list programmatically
Head –> 10 –> 8 –> 1 –> 11 –> null
Algorithm & Execution
current = head;
int count = 0;
while(current != null)
{
count++;
current = current.next;
}
- We will create a temporary node called current, initially pointing to null.
- The current node is set to point to the head of the linked list.
- We create a count variable to keep track of the number of nodes present in the list, initialized as int count = 0.
- Execute the while loop until the current node reaches the end of the list. We check if the current node equals null; if yes, it means we’ve reached the end. For each iteration, we increment the count variable by 1, and the current node pointer moves to the next node.
Code
// Given a ListNode head, find out the length of the linked list
public int length(ListNode head) {
if (head == null) {
return 0;
}
// Create a count variable to hold the length
int count = 0;
// Loop through each element and increment the count until the list ends
ListNode current = head;
whi le (current != null) {
count++;
// Move to the next node
current = current.next;
}
return count;
}
Here, code snippet defines a method length
within the SinglyLinkedList
class, which calculates the length of the linked list starting from the provided head node. It iterates through the linked list nodes, incrementing a count variable for each node encountered, until it reaches the end of the list. Finally, it returns the count representing the length of the linked list.
Singly Linked List Insertion Operation
One of the primary operations performed on singly linked lists is insertion, which involves adding new nodes at various positions within the list. Let’s explore their complexities, implementation techniques, and best practices.
How to inser node at the beginning of singly linked list in java
Inserting a new node at the beginning of a singly linked list is a straightforward operation. It involves creating a new node with the desired data value and adjusting the pointers to ensure proper linkage.
Head –> 10 –> 8 –> 1 –> 11 –> null
Algorithm & Execution
ListNode newNode = new ListNode(15);
newNode.next = head;
head = newNode;
- The first step creates a new node with data 15 and next pointing to null, i.e., newNode –> 15 –> null.
- To insert the new node at the beginning of the list, update the next pointer of the new node to point to the current head node. This means connecting the new node’s next reference to the head node of the list, i.e., newNode –> 15 –> head.
- The final step updates the head pointer to point to the new node, making the new node the head node of the linked list.After execution, the linked list becomes: head –> 15 –> 10 –> 8 –> 1 –> 11 –> null.”
Code
public ListNode insertAtBeginning(ListNode head, int data) {
ListNode newNode = new ListNode(data);
if (head == null) {
return newNode;
}
newNode.next = head;
head = newNode;
return head; // this head will be the new head, having the new node at the beginning
}
This method insertAtBeginning
is designed to insert a new node with the provided data at the beginning of the linked list. If the list is initially empty (i.e., head
is null), the new node becomes the head of the list. Otherwise, the new node is inserted before the current head, and the head pointer is updated to point to the new node. Finally, the updated head is returned.
How to insert a node at the end of singly linked list in java?
Inserting a new node at the end of a singly linked list requires traversing the list until reaching the last node, then updating the last node’s reference to point to the new node.
Algorithm & Execution
ListNode newNode = new ListNode(15);
ListNode current = head;
while(null != current.next)
{
current = current.next;
}
current.next = newNode;
- Creates a new node with data 15 and next pointing to null, i.e., newNode –> 15 –> null.
- To insert the new node at the end of the list, we need to traverse the list till the end and then assign the new node to the last node’s next pointer. For this, we create a temporary node called current, which initially points to null. Then we make this current node point to the head of the list, i.e., ListNode current = head.
- We use a while loop to reach the last node of the list. For each iteration, we check whether the current node’s next pointer points to null or not. Also, for each iteration, we update current to its next pointer. When we reach the last node, we stop and terminate the while loop.
while(current.next != null)
{
current = current.next;
}
Finally, we assign the new node to current.next pointer because at this point, current is the last node of the list.
Code
public ListNode insertAtEnd(ListNode head, int data) {
ListNode newNode = new ListNode(data);
if (head == null) {
return newNode;
}
ListNode current = head;
// Loop until the last node of the list (Note: it's not until the end of the list; in this case, current == null. Here, current.next being null means we're at the last node of the list.)
while (current.next != null) {
current = current.next; // Move to the next node
}
current.next = newNode; // Assign the new node to the last node
return head;
}
This method insertAtEnd
is designed to insert a new node with the provided data at the end of the linked list. If the list is initially empty (i.e., head
is null), the new node becomes the head of the list. Otherwise, it traverses the list until it reaches the last node. Then, it assigns the new node as the next node of the last node and returns the head of the list.
How to insert a node in singly linked list after a given node in java ?
In this scenario, the initial step involves traversing the linked list until the preceding node of the insertion point is reached. Subsequently, node pointers are adjusted accordingly.
Algorithm & Execution
ListNode newNode = new ListNode(15);
newNode.next = previous.next;
previous.next = newNode;
- Creates a new node with data 15 and its next pointer pointing to null.
- Since we’re inserting the new node between the specified previous node and its next node, we first assign the previous node’s next pointer to the new node’s next pointer. This means connecting the new node’s next pointer to the node that was originally after the previous node, i.e., newNode –> 15 –> 11 –> null.
- Finally, we update the previous node’s next pointer to point to the new node, so that the new node is placed between the specified previous node and its next node.After execution, the linked list becomes: head –> 10 –> 8 –> 15 –> 11 –> null.
Code
public void insertAfter(ListNode previous, int data) {
if (previous == null) {
System.out.println("The given previous node cannot be null.");
return;
}
ListNode newNode = new ListNode(data);
newNode.next = previous.next;
previous.next = newNode;
}
This method insertAfter
is designed to insert a new node with the provided data after a specified node in the linked list. It first checks if the given previous node is not null. If it is null, it prints an error message and returns. Otherwise, it creates a new node with the provided data, links it to the node following the specified previous node, and then updates the next reference of the previous node to point to the new node.
How to insert a node in singly linked list at a given position in java?
Inserting a node at a specific position in a singly linked list involves traversing the list to find the desired position, then adjusting the pointers accordingly.
head –> 10 –> 8 –> 11 –> null
Given a linked list with three nodes, where the first node contains the value 10 and points to position 1, the second node contains the value 8 and points to position 2, we need to insert a new node at position 3. Therefore, to insert the new node at position 3, we must traverse the linked list until we reach position 2 and then insert the new node after position 2.
Algorithm & Execution
ListNode newNode = new ListNode(15);
ListNode previous = head;
int count = 1;
while(count < position - 1)
{
previous = previous.next;
count++;
}
ListNode current = previous.next;
newNode.next = current;
previous.next = newNode;
- Creates a new node with data 15 and its next pointer pointing to null, i.e., 15 –> null.
- To insert the new node at the given position, we need to traverse until position – 1.
- We create a temporary node named previous and point it to the head of the list, i.e., previous –> head.
- To track the number of nodes traversed, we create a count variable and initialize it to 1, because the first node has already been traversed by the previous node, i.e., int count = 1.
- Let’s execute a few steps within the while loop. We traverse until just before the given position node, checking how many nodes we’ve traversed using the while loop until position – 1.
while(count < position - 1) // When we reach position - 1, the loop will terminate (when count = 2), and at this point, our 'previous' will point to position - 1.
{
previous = previous.next;
count++; // As 'previous' moves forward, we increment count by one.
}
- Moving ahead, we create a temporary node named current, which will hold the next node after ‘previous’, i.e., current –> 11, and previous –> 8.
- The next pointer of the new node will point to the ‘current’ node, establishing a new connection between the new node and the ‘current’ node, i.e., newNode –> 15 –> 11.
- The final step is to set the next pointer of ‘previous’ to point to the new node, i.e., previous –> 8 –> newNode –> 15.
Output: head –> 10 –> 8 –> 15 –> 11 –> null. So, we inserted 15 at position 3, where ‘previous’ –> 8 and ‘current’ –> 11, and newNode –> 15 will be between the ‘previous’ and ‘current’ node.
Code
public ListNode insertAtPosition(ListNode head, int data, int position) {
// Perform boundary checks
int size = length(head); // We use the already defined length method which returns the size of the list
if (position > size + 1 || position < 1) { // If the position is greater than the number of nodes in the list or less than 1, then it is an invalid position
System.out.println("Invalid position");
return head;
}
ListNode newNode = new ListNode(data);
if (position == 1) { // If position == 1, it means the list contains only one node and we want to insert at that position. Then we assign newNode next to head and return newNode as the new head of the list
newNode.next = head;
return newNode;
}
else {
// Else we perform our regular algorithm
ListNode previous = head;
int count = 1;
while (count < position - 1) {
previous = previous.next;
count++;
}
ListNode current = previous.next;
newNode.next = current;
previous.next = newNode;
return head;
}
}
}
This method insertAtPosition
is designed to insert a new node with the provided data at a specified position in the linked list. It first performs boundary checks to ensure that the position is valid. If the position is valid, it creates a new node with the provided data. If the position is 1, indicating that the node needs to be inserted at the beginning of the list, it updates the new node’s next reference to the current head and returns the new node as the new head of the list. Otherwise, it traverses the list to the node just before the specified position, updates the next reference of the new node to the current node at the specified position, and updates the next reference of the previous node to the new node. Finally, it returns the head of the list.
Singly Linked List Deletion Operation
Deletion operations play a crucial role in managing linked lists efficiently. In this section, we’ll delve into the intricacies of deleting nodes from singly linked lists, exploring various scenarios, implementation techniques, and best practices.
How to delete first node from a singly linked list in java?
Deleting a node from the beginning of a singly linked list is a straightforward operation. It involves updating the reference of the head node to point to the next node in the list.
head –> 10 –> 8 –> 1 –> 11 –> null;
Algorithm & Execution
ListNode temp = head;
head = head.next;
temp.next = null;
- Let’s create a temporary node named
temp
and point it tohead
, i.e.,ListNode temp = head
. - In order to delete the first node from the list, we need to remove the reference to the first node from the
head
pointer. Thus, we update thehead
pointer to point to the next node in the list, i.e.,head = head.next
. - We disconnect the first node from the linked list by assigning
null
to thetemp
node’s next pointer, i.e.,temp.next = null
.
Output: head --> 8 --> 1 --> 11 --> null
.
Initially, the list has 5 nodes. After deleting the first node, only three nodes remain.
Code
public ListNode deleteFirst(ListNode head) {
if (head == null) {
return head;
}
ListNode temp = head;
head = head.next;
temp.next = null;
return temp;
}
This method deleteFirst
is designed to delete the first node of the linked list. If the list is empty (i.e., head
is null), it returns null. Otherwise, it stores the reference to the current head node in a temporary variable temp
, updates the head to the next node in the list, sets the next reference of the original head to null, and then returns the deleted node.
How to delete last node of singly linked list in java?
Deleting a node from the end of a singly linked list requires traversing the list until reaching the second-to-last node, then updating its reference to NULL.
head –> 10 –> 8 –> 11 –> null
Algorithm & Execution
ListNode last = head;
ListNode previousToLast = null;
while(last.next != null)
{
previousToLast = last;
last = last.next;
}
previousToLast.next = null; // Disconnect the last node from the list
- First, we create a temporary node called
last
, which initially points to the head node, i.e.,ListNode last = head
. - We create another temporary node called
previousToLast
and assignnull
to it. - We traverse until the
last
node’s next pointer points tonull
, so thatlast
becomes the last node of the list andpreviousToLast
points to the second last node of the list.
while(last.next != null)
{
previousToLast = last; // Becoming the previous node of the current last node
last = last.next; // Move to the next node to become the last node for every iteration.
}
- In the final step, we disconnect the second last node from the last node by assigning
null
to its next pointer, i.e.,previousToLast.next = null
. - Optionally, you can return the deleted last node if such a requirement exists, for example, for printing purposes.
Code
public ListNode deleteLast(ListNode head) {
if (head == null) {
return head;
}
ListNode last = head;
ListNode previousToLast = null;Singly
while (last.next != null) {
previousToLast = last;
last = last.next;
}
previousToLast.next = null;
return last;
}
This method deleteLast
is designed to delete the last node of the linked list. If the list is empty (i.e., head
is null), it returns null. Otherwise, it traverses the list to find the last node (last
) and the node just before it (previousToLast
). It then updates the next reference of previousToLast
to null, effectively removing the last node from the list. Finally, it returns the deleted node.
How to delete a node from Singly Linked List at a given position in Java?
Deleting a specific node from a singly linked list involves finding the node to be deleted and adjusting the pointers of its neighboring nodes to bypass it.
head –> 10 –> 8 –> 15 –> 11 –> null
Algorithm & Execution
Listwhile(count < position - 1)
{
previous = previous.next;
count++;
}
Node previous = head;
int count = 1;
while(count < position - 1)
{
previous = previous.next;
count++;
}
ListNode current = previous.next;
previous.next = current.next;
current.next = null;
- We create a temporary node called
previous
, which initially points to the head. - To keep track of the number of nodes we traverse, we use a
count
variable. Initially, it’s 1 because the first node has already been traversed by theprevious
node, which points to 10. - We traverse until the node before the position we want to delete. This means we stop at position – 1.
while(count < position - 1)
{
previous = previous.next;
count++;
}
- When the while loop terminates, we are at position – 1, which is the node before the node we want to delete.
- We create a temporary node called
current
, which holds the reference of the node to be deleted. - To delete the node, we make
previous.next
point tocurrent.next
, effectively bypassing the current node in the linked list. - Finally, we set
current.next
tonull
to disconnect it from the list.
Output: head --> 10 --> 8 --> 11 --> null
.
As a result of deleting the node at position 3, which contains 15, the linked list becomes head --> 10 --> 8 --> 11 --> null
.
Code
public ListNode deleteAtPosition(ListNode head, int position) {
int size = length(head); // Used the already defined method to find out the size of the list
if (position > size || position < 1) {
System.out.println("Invalid position");
}
if (position == 1) {
// If position is 1, it means we will apply delete first node logic
ListNode temp = head;
head = head.next;
temp.next = null;
return temp;
}
else {
ListNode previous = head;
int count = 1;
while (count < position - 1) {
previous = previous.next;
count++;
}
ListNode current = previous.next;
previous.next = current.next;
current.next = null;
return current;
}
}
This method deleteAtPosition
is designed to delete a node at the specified position in the linked list. It first finds the size of the list using the already defined length
method and performs a boundary check to ensure that the position is valid. If the position is invalid, it prints an error message. If the position is 1, indicating that the first node needs to be deleted, it applies the logic of deleting the first node. Otherwise, it traverses the list to find the node just before the specified position (previous
). It then updates the next reference of previous
to skip the node to be deleted and returns the deleted node.
Conclusion
By mastering singly linked lists in Java, you’ll not only enhance your understanding of fundamental data structures but also improve your problem-solving skills and become a more proficient programmer. With the comprehensive knowledge provided in this guide, you’ll be well-equipped to tackle a wide range of programming challenges and develop efficient and elegant solutions.