Master advanced linked list operations through hands-on implementation and practical exercises.
In this guided project, you'll implement advanced linked list operations with a focus on general insertion techniques. You'll learn how to handle edge cases, maintain list integrity, and optimize your implementations.
Initialize a new node with the given value
Node newNode = new Node(value);
Check for empty list and invalid positions
if (position < 0 || position > size) {
return false;
}
if (head == null && position == 0) {
head = newNode;
size++;
return true;
}
Navigate to the insertion point
Node current = head;
for (int i = 0; i < position - 1; i++) {
current = current.next;
}
Update references to insert the new node
newNode.next = current.next;
current.next = newNode;
size++;
When implementing linked list operations, keep these important points in mind:
The sequence of operations is crucial, especially when modifying references:
// CORRECT order for insertion
newNode.next = current.next; // Set new node's reference first
current.next = newNode; // Then update the current node's reference
// INCORRECT order would lose the rest of the list
// current.next = newNode; // DON'T do this first
// newNode.next = current.next; // This would point to itself!
Imagine insertion at position 2 in a list:
// Before: head -> A -> B -> C -> null
// 0 1 2
// Insert X at position 2
// After: head -> A -> B -> X -> C -> null
// 0 1 2 3
Steps to achieve this:
When traversing to nodes for insertion or deletion, be careful with zero-based indexing:
// To insert at position 2, we need to stop at position 1
Node current = head;
// Loop stops when i equals position-1
for (int i = 0; i < position - 1; i++) {
current = current.next;
}
// Now current is at position 1, and we insert after it
Always check for null before dereferencing:
// INCORRECT - might cause NullPointerException
current.next = newNode;
// CORRECT - check first
if (current != null) {
current.next = newNode;
} else {
// Handle the error case
}
In Java, detached nodes are automatically garbage-collected. However, in other languages, you might need to explicitly free memory.
To ensure clean removal in any language:
// When removing node B in: A -> B -> C
// First update references
A.next = C;
// Then ensure B has no lingering references (important in some languages)
B.next = null; // In Java, this isn't strictly necessary but is good practice
Implement a function to insert a node at any valid position in a linked list.
public boolean insert(int value, int position) {
// Your implementation here
}
Implement a function to insert a node into a sorted linked list while maintaining the order.
public void insertSorted(int value) {
// Your implementation here
}
// Given sorted list: 10 -> 20 -> 30 -> 40
// Insert value: 25
// After insertion: 10 -> 20 -> 25 -> 30 -> 40
// ^-- insert here
// Implementation logic:
// 1. Start at head (10)
// 2. Check: 10 < 25, move to next (20)
// 3. Check: 20 < 25, move to next (30)
// 4. Check: 30 > 25, found position! Insert between 20 and 30
Implement a function to remove duplicate values from a linked list.
public void removeDuplicates() {
// Your implementation here
}
// Given list: 1 -> 2 -> 2 -> 3 -> 1 -> 4
// After removing duplicates: 1 -> 2 -> 3 -> 4
// Implementation logic:
// 1. Start with first node (1)
// a. Check all nodes ahead for duplicates of 1
// b. Find duplicate at position 4, remove it
// 2. Move to second node (2)
// a. Check all nodes ahead for duplicates of 2
// b. Find duplicate at position 2, remove it
// 3. Continue with remaining nodes...
For a more efficient solution, use a hash set to track seen values:
public void removeDuplicatesWithHashSet() {
if (head == null) return;
HashSet<Integer> seenValues = new HashSet<>();
Node current = head;
Node previous = null;
while (current != null) {
// If value has been seen before, remove node
if (seenValues.contains(current.value)) {
previous.next = current.next;
size--;
} else {
// Add to set and move previous pointer
seenValues.add(current.value);
previous = current;
}
current = current.next;
}
}
Implement a function to reverse a linked list in-place.
public void reverse() {
// Your implementation here
}
// Given list: 1 -> 2 -> 3 -> 4 -> null
// After reversal: 4 -> 3 -> 2 -> 1 -> null
// Implementation logic:
// Initial state:
// prev=null, current=1, next=null
// Step 1:
// next = current.next = 2
// current.next = prev = null, so 1 -> null
// prev = current = 1
// current = next = 2
// State: null <- 1, 2 -> 3 -> 4
// Step 2:
// next = current.next = 3
// current.next = prev = 1, so 2 -> 1
// prev = current = 2
// current = next = 3
// State: null <- 1 <- 2, 3 -> 4
// Continue until current is null
// Final step: update head = prev (which is 4)