Master advanced linked list operations including searching, insertion, and deletion in Java. Learn efficient techniques for manipulating linked data structures.
Searching a linked list involves traversing the list from the head node until either finding the target value or reaching the end of the list. Since nodes in a linked list aren't stored in contiguous memory locations, you must follow the references from one node to the next.
/**
* Searches for a value in the linked list
* @param value the value to search for
* @return the first node containing the value, or null if not found
*/
public Node<T> search(T value) {
// Handle edge case: empty list
if (head == null) {
return null;
}
// Start at the head
Node<T> current = head;
// Traverse the list
while (current != null) {
// Check if current node contains the value
if (current.getValue().equals(value)) {
return current; // Value found
}
// Move to the next node
current = current.getNext();
}
// Value not found in the list
return null;
}
Insertion in a linked list requires creating a new node and adjusting the references of existing nodes to maintain the list structure. There are three main insertion scenarios: at the beginning, in the middle, or at the end.
/**
* Inserts a new node at the beginning of the list
* @param value the value to insert
*/
public void insertAtBeginning(T value) {
// Create a new node
Node<T> newNode = new Node<>(value);
// Set the new node's next pointer to the current head
newNode.setNext(head);
// Update the head to be the new node
head = newNode;
// Increment the length
length++;
}
/**
* Inserts a new node at the end of the list
* @param value the value to insert
*/
public void insertAtEnd(T value) {
// Create a new node
Node<T> newNode = new Node<>(value);
// If the list is empty, make the new node the head
if (head == null) {
head = newNode;
} else {
// Traverse to the last node
Node<T> current = head;
while (current.getNext() != null) {
current = current.getNext();
}
// Link the last node to the new node
current.setNext(newNode);
}
// Increment the length
length++;
}
/**
* Inserts a new node at a specific position
* @param value the value to insert
* @param index the position at which to insert (0-based)
* @return true if insertion was successful, false otherwise
*/
public boolean insertAtPosition(T value, int index) {
// Validate the index
if (index < 0 || index > length) {
return false;
}
// Handle insertion at the beginning
if (index == 0) {
insertAtBeginning(value);
return true;
}
// Handle insertion at the end
if (index == length) {
insertAtEnd(value);
return true;
}
// Handle insertion in the middle
Node<T> newNode = new Node<>(value);
Node<T> current = head;
// Traverse to the node just before the insertion point
for (int i = 0; i < index - 1; i++) {
current = current.getNext();
}
// Insert the new node
newNode.setNext(current.getNext());
current.setNext(newNode);
// Increment the length
length++;
return true;
}
Deletion in a linked list involves removing a node and reconnecting the list to maintain the chain. This requires adjusting pointers and handling several edge cases.
/**
* Deletes the first node in the list
* @return true if deletion was successful, false if the list was empty
*/
public boolean deleteFirst() {
// Check if the list is empty
if (head == null) {
return false;
}
// Update the head to the second node
head = head.getNext();
// Decrement the length
length--;
return true;
}
/**
* Deletes the last node in the list
* @return true if deletion was successful, false if the list was empty
*/
public boolean deleteLast() {
// Check if the list is empty
if (head == null) {
return false;
}
// If there's only one node, delete the head
if (head.getNext() == null) {
head = null;
length--;
return true;
}
// Traverse to the second-to-last node
Node<T> current = head;
while (current.getNext().getNext() != null) {
current = current.getNext();
}
// Remove the last node by updating second-to-last's next pointer
current.setNext(null);
// Decrement the length
length--;
return true;
}
/**
* Deletes a node at a specific position
* @param index the position of the node to delete (0-based)
* @return true if deletion was successful, false otherwise
*/
public boolean deleteAtPosition(int index) {
// Validate the index
if (index < 0 || index >= length || head == null) {
return false;
}
// Handle deletion of the head
if (index == 0) {
return deleteFirst();
}
// Handle deletion of the last node
if (index == length - 1) {
return deleteLast();
}
// Traverse to the node just before the one to delete
Node<T> current = head;
for (int i = 0; i < index - 1; i++) {
current = current.getNext();
}
// Update references to skip over the node to be deleted
current.setNext(current.getNext().getNext());
// Decrement the length
length--;
return true;
}
/**
* Deletes the first occurrence of a node with the specified value
* @param value the value to find and delete
* @return true if a node was deleted, false if the value wasn't found
*/
public boolean deleteByValue(T value) {
// Check if the list is empty
if (head == null) {
return false;
}
// Check if the head contains the value
if (head.getValue().equals(value)) {
head = head.getNext();
length--;
return true;
}
// Traverse the list looking for the value
Node<T> current = head;
while (current.getNext() != null) {
if (current.getNext().getValue().equals(value)) {
// Found the value in the next node, so skip over it
current.setNext(current.getNext().getNext());
length--;
return true;
}
current = current.getNext();
}
// Value not found
return false;
}
In Java, the garbage collector automatically reclaims memory for nodes that are no longer referenced. When you remove a node from the list by updating references to bypass it, and no other references to that node exist, it becomes eligible for garbage collection.
public class Node {
private T value;
private Node next;
public Node(T value) {
this.value = value;
this.next = null;
}
public T getValue() {
return value;
}
public Node getNext() {
return next;
}
public void setNext(Node next) {
this.next = next;
}
}
public class LinkedList {
private Node head;
private int length;
public LinkedList() {
this.head = null;
this.length = 0;
}
public Node search(T value) {
Node current = head;
while (current != null) {
if (current.getValue().equals(value)) {
return current;
}
current = current.getNext();
}
return null;
}
public boolean insert(T value, int index) {
if (index < 0 || index > length) {
return false;
}
Node newNode = new Node<>(value);
if (index == 0) {
newNode.setNext(head);
head = newNode;
} else {
Node current = head;
for (int i = 0; i < index - 1; i++) {
current = current.getNext();
}
newNode.setNext(current.getNext());
current.setNext(newNode);
}
length++;
return true;
}
public boolean delete(int index) {
if (index < 0 || index >= length || head == null) {
return false;
}
if (index == 0) {
head = head.getNext();
} else {
Node current = head;
for (int i = 0; i < index - 1; i++) {
current = current.getNext();
}
current.setNext(current.getNext().getNext());
}
length--;
return true;
}
}
For additional hands-on practice and advanced exercises on linked lists, check out our comprehensive guided project:
Implement a search method that finds the first occurrence of a value in the linked list and returns its index.
public int findIndex(T value) {
// Your implementation here
}
Implement a method to insert a node at a specific position while handling all edge cases.
public boolean insertAt(T value, int position) {
// Your implementation here
}
Implement a method to delete the first occurrence of a specific value in the linked list.
public boolean deleteValue(T value) {
// Your implementation here
}