Remove Nodes From Linked List
MedPrompt
You are given the head of a linked list.
Remove every node which has a node with a greater value anywhere to the right side of it.
Return the head of the modified linked list.
Example 1:
Input: head = [5,2,13,3,8]
Output: [13,8]
Explanation: The nodes that should be removed are 5, 2 and 3.
- Node 13 is to the right of node 5.
- Node 13 is to the right of node 2.
- Node 8 is to the right of node 3.Example 2:
Input: head = [1,1,1,1]
Output: [1,1,1,1]
Explanation: Every node has value 1, so no nodes are removed.
Constraints:
- The number of the nodes in the given list is in the range
[1, 105]. 1 <= Node.val <= 105
Approaches
4 approaches with complexity analysis and trade-offs.
This approach uses a straightforward, brute-force method. It iterates through each node of the linked list, and for each node, it performs a second scan through all the subsequent nodes to check if there exists any node with a strictly greater value. If a greater value is found, the current node is removed from the list.
Algorithm
- Create a
dummynode that points to theheadto simplify edge cases like removing the original head. - Use two pointers,
prevandcurrent.prevwill point to the last node that was kept, andcurrentwill iterate through the list. - For each
currentnode, start another pointerrunnerfromcurrent.next. - Iterate with
runnerthrough the rest of the list. If anyrunner.val > current.val, it meanscurrentmust be removed. - If
currentneeds to be removed, updateprev.nextto point tocurrent.next, effectively skippingcurrent. - If
currentdoes not need to be removed (i.e., the inner loop finishes without finding a greater value), thencurrentis a valid node in the final list, so we updateprevtocurrent. - Advance
currentto the next node in the list and repeat the process. - Finally, return
dummy.nextwhich points to the head of the modified list.
Walkthrough
The core idea is to check the removal condition for each node one by one. We can use a dummy head to make the removal of the first node easier. We iterate through the list with a current pointer. For each current node, we use a runner pointer to scan the rest of the list to its right. If the runner finds any node with a value greater than current.val, we know current must be removed. To remove it, we need a pointer to the node before current, let's call it prev. We update prev.next to current.next. If the entire scan to the right of current completes without finding a greater node, we keep current and simply advance prev to current. This nested loop structure leads to a quadratic time complexity.
/** * Definition for singly-linked list. * public class ListNode { * int val; * ListNode next; * ListNode() {} * ListNode(int val) { this.val = val; } * ListNode(int val, ListNode next) { this.val = val; this.next = next; } * } */class Solution { public ListNode removeNodes(ListNode head) { ListNode dummy = new ListNode(0, head); ListNode current = head; ListNode prev = dummy; while (current != null) { boolean foundGreater = false; ListNode runner = current.next; while (runner != null) { if (runner.val > current.val) { foundGreater = true; break; } runner = runner.next; } if (foundGreater) { // Remove current node by linking prev to current's next prev.next = current.next; } else { // Keep current node, so update prev prev = current; } // Move to the next node in the original list current = current.next; } return dummy.next; }}Complexity
Time
O(N^2) - For each node in the list (N nodes), we may traverse the rest of the list (up to N-1 nodes). This results in a nested loop structure, giving a quadratic time complexity.
Space
O(1) - We only use a constant number of extra pointers (`dummy`, `prev`, `current`, `runner`) regardless of the list size.
Trade-offs
Pros
The logic is simple and easy to understand.
It operates in-place and uses O(1) extra space.
Cons
The time complexity of O(N^2) is highly inefficient and will likely result in a 'Time Limit Exceeded' error for large inputs as specified in the constraints (N up to 10^5).
Solutions
Solution
/** * Definition for singly-linked list. * public class ListNode { * int val; * ListNode next; * ListNode() {} * ListNode(int val) { this.val = val; } * ListNode(int val, ListNode next) { this.val = val; this.next = next; } * } */ class Solution { public ListNode removeNodes ( ListNode head ) { List < Integer > nums = new ArrayList <>(); while ( head != null ) { nums . add ( head . val ); head = head . next ; } Deque < Integer > stk = new ArrayDeque <>(); for ( int v : nums ) { while (! stk . isEmpty () && stk . peekLast () < v ) { stk . pollLast (); } stk . offerLast ( v ); } ListNode dummy = new ListNode (); head = dummy ; while (! stk . isEmpty ()) { head . next = new ListNode ( stk . pollFirst ()); head = head . next ; } return dummy . next ; } }Video walkthrough
Newsletter
One sharp idea, every week
System design and interview prep — short enough to finish.
No spam. Unsubscribe anytime.
Practice
Same difficulty — related problems to reinforce the pattern.