Before attempting this problem, you should be comfortable with:
Linked List Fundamentals - Understanding singly linked list structure, traversal, and pointer manipulation
Monotonic Stack - Using a stack that maintains elements in strictly increasing or decreasing order
Linked List Reversal - Reversing a linked list in-place using iterative pointer swapping
1. Convert To Array
Intuition
A node should be removed if there exists a larger value somewhere to its right. To check this efficiently, we first convert the linked list into an array. Then we traverse the array from right to left, keeping track of the maximum value seen so far. Any node with a value smaller than this running maximum gets removed by adjusting the previous node's pointer.
Algorithm
Create an array with a dummy node at the start, followed by all nodes from the linked list.
Initialize rightMaxi to track the maximum node seen from the right.
Traverse the array from right to left:
If the current node's value is less than rightMaxi.val, update the previous node's next pointer to skip it.
/**
* Definition for singly-linked list.
* class ListNode {
* constructor(val = 0, next = null) {
* this.val = val;
* this.next = next;
* }
* }
*/classSolution{/**
* @param {ListNode} head
* @return {ListNode}
*/removeNodes(head){let cur = head,
arr =[{val:0,next: head }];while(cur){
arr.push(cur);
cur = cur.next;}let rightMaxi ={val:0,next:null};for(let i = arr.length -1; i >0; i--){if(rightMaxi.val > arr[i].val){
arr[i -1].next = rightMaxi;}else{
rightMaxi = arr[i];}}return arr[0].next;}}
/**
* Definition for singly-linked list.
* public class ListNode {
* public int val;
* public ListNode next;
* public ListNode(int val=0, ListNode next=null) {
* this.val = val;
* this.next = next;
* }
* }
*/publicclassSolution{publicListNodeRemoveNodes(ListNode head){var arr =newList<ListNode>();
arr.Add(newListNode(0, head));var cur = head;while(cur !=null){
arr.Add(cur);
cur = cur.next;}var rightMaxi =newListNode(0,null);for(int i = arr.Count -1; i >0; i--){if(rightMaxi.val > arr[i].val){
arr[i -1].next = rightMaxi;}else{
rightMaxi = arr[i];}}return arr[0].next;}}
/**
* Definition for singly-linked list.
* type ListNode struct {
* Val int
* Next *ListNode
* }
*/funcremoveNodes(head *ListNode)*ListNode {
arr :=[]*ListNode{{Val:0, Next: head}}
cur := head
for cur !=nil{
arr =append(arr, cur)
cur = cur.Next
}
rightMaxi :=&ListNode{Val:0, Next:nil}for i :=len(arr)-1; i >0; i--{if rightMaxi.Val > arr[i].Val {
arr[i-1].Next = rightMaxi
}else{
rightMaxi = arr[i]}}return arr[0].Next
}
/**
* Example:
* var li = ListNode(5)
* var v = li.`val`
* Definition for singly-linked list.
* class ListNode(var `val`: Int) {
* var next: ListNode? = null
* }
*/class Solution {funremoveNodes(head: ListNode?): ListNode?{val arr = mutableListOf<ListNode>()val dummy =ListNode(0)
dummy.next = head
arr.add(dummy)var cur = head
while(cur !=null){
arr.add(cur)
cur = cur.next
}var rightMaxi =ListNode(0)for(i in arr.size -1 downTo 1){if(rightMaxi.`val` > arr[i].`val`){
arr[i -1].next = rightMaxi
}else{
rightMaxi = arr[i]}}return arr[0].next
}}
/**
* Definition for singly-linked list.
* public class ListNode {
* public var val: Int
* public var next: ListNode?
* public init() { self.val = 0; self.next = nil; }
* public init(_ val: Int) { self.val = val; self.next = nil; }
* public init(_ val: Int, _ next: ListNode?) { self.val = val; self.next = next; }
* }
*/classSolution{funcremoveNodes(_ head:ListNode?)->ListNode?{var arr:[ListNode]=[ListNode(0, head)]var cur = head
while cur !=nil{
arr.append(cur!)
cur = cur?.next
}var rightMaxi =ListNode(0,nil)for i instride(from: arr.count -1, through:1, by:-1){if rightMaxi.val > arr[i].val {
arr[i -1].next = rightMaxi
}else{
rightMaxi = arr[i]}}return arr[0].next
}}
Time & Space Complexity
Time complexity: O(n)
Space complexity: O(n)
2. Monotonic Decreasing Stack
Intuition
We need to keep only nodes that have no larger value to their right. A monotonic decreasing stack naturally maintains this property. As we traverse the list, we pop any stack elements that are smaller than the current node's value. This ensures the stack always contains values in decreasing order from bottom to top, which are exactly the nodes that should remain.
Algorithm
Initialize an empty stack to store node values.
Traverse the linked list:
While the stack is not empty and the current value is greater than the top of the stack, pop from the stack.
Push the current value onto the stack.
Build a new linked list from the stack values in order.
# Definition for singly-linked list.# class ListNode:# def __init__(self, val=0, next=None):# self.val = val# self.next = nextclassSolution:defremoveNodes(self, head: Optional[ListNode])-> Optional[ListNode]:
stack =[]
cur = head
while cur:while stack and cur.val > stack[-1]:
stack.pop()
stack.append(cur.val)
cur = cur.next
dummy = ListNode()
cur = dummy
for num in stack:
cur.next= ListNode(num)
cur = cur.nextreturn dummy.next
/**
* 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; }
* }
*/publicclassSolution{publicListNoderemoveNodes(ListNode head){Stack<Integer> stack =newStack<>();ListNode cur = head;while(cur !=null){while(!stack.isEmpty()&& cur.val > stack.peek()){
stack.pop();}
stack.push(cur.val);
cur = cur.next;}ListNode dummy =newListNode();
cur = dummy;for(int num : stack){
cur.next =newListNode(num);
cur = cur.next;}return dummy.next;}}
/**
* Definition for singly-linked list.
* class ListNode {
* constructor(val = 0, next = null) {
* this.val = val;
* this.next = next;
* }
* }
*/classSolution{/**
* @param {ListNode} head
* @return {ListNode}
*/removeNodes(head){let stack =[];let cur = head;while(cur){while(stack.length && cur.val > stack[stack.length -1]){
stack.pop();}
stack.push(cur.val);
cur = cur.next;}let dummy =newListNode();
cur = dummy;for(let num of stack){
cur.next =newListNode(num);
cur = cur.next;}return dummy.next;}}
/**
* Definition for singly-linked list.
* public class ListNode {
* public int val;
* public ListNode next;
* public ListNode(int val=0, ListNode next=null) {
* this.val = val;
* this.next = next;
* }
* }
*/publicclassSolution{publicListNodeRemoveNodes(ListNode head){var stack =newList<int>();var cur = head;while(cur !=null){while(stack.Count >0&& cur.val > stack[stack.Count -1]){
stack.RemoveAt(stack.Count -1);}
stack.Add(cur.val);
cur = cur.next;}var dummy =newListNode();
cur = dummy;foreach(int num in stack){
cur.next =newListNode(num);
cur = cur.next;}return dummy.next;}}
/**
* Definition for singly-linked list.
* type ListNode struct {
* Val int
* Next *ListNode
* }
*/funcremoveNodes(head *ListNode)*ListNode {
stack :=[]int{}
cur := head
for cur !=nil{forlen(stack)>0&& cur.Val > stack[len(stack)-1]{
stack = stack[:len(stack)-1]}
stack =append(stack, cur.Val)
cur = cur.Next
}
dummy :=&ListNode{}
cur = dummy
for_, num :=range stack {
cur.Next =&ListNode{Val: num}
cur = cur.Next
}return dummy.Next
}
/**
* Example:
* var li = ListNode(5)
* var v = li.`val`
* Definition for singly-linked list.
* class ListNode(var `val`: Int) {
* var next: ListNode? = null
* }
*/class Solution {funremoveNodes(head: ListNode?): ListNode?{val stack = mutableListOf<Int>()var cur = head
while(cur !=null){while(stack.isNotEmpty()&& cur.`val` > stack.last()){
stack.removeAt(stack.size -1)}
stack.add(cur.`val`)
cur = cur.next
}val dummy =ListNode(0)var node: ListNode?= dummy
for(num in stack){
node?.next =ListNode(num)
node = node?.next
}return dummy.next
}}
/**
* Definition for singly-linked list.
* public class ListNode {
* public var val: Int
* public var next: ListNode?
* public init() { self.val = 0; self.next = nil; }
* public init(_ val: Int) { self.val = val; self.next = nil; }
* public init(_ val: Int, _ next: ListNode?) { self.val = val; self.next = next; }
* }
*/classSolution{funcremoveNodes(_ head:ListNode?)->ListNode?{var stack =[Int]()var cur = head
while cur !=nil{while!stack.isEmpty && cur!.val > stack.last!{
stack.removeLast()}
stack.append(cur!.val)
cur = cur?.next
}let dummy =ListNode()var node:ListNode?= dummy
for num in stack {
node?.next =ListNode(num)
node = node?.next
}return dummy.next
}}
Time & Space Complexity
Time complexity: O(n)
Space complexity: O(n)
3. Recursion
Intuition
Recursion lets us process the list from right to left naturally. We first recurse to the end, then on the way back, each node compares itself to whatever remains in the processed suffix. If the next node has a larger value, the current node should be removed, so we return head.next instead. Otherwise, we keep the current node.
Algorithm
Base case: if head is null, return null.
Recursively process head.next and assign the result back to head.next.
If head.next exists and head.val < head.next.val, return head.next (remove current node).
/**
* Definition for singly-linked list.
* public class ListNode {
* public int val;
* public ListNode next;
* public ListNode(int val=0, ListNode next=null) {
* this.val = val;
* this.next = next;
* }
* }
*/publicclassSolution{publicListNodeRemoveNodes(ListNode head){if(head ==null)returnnull;
head.next =RemoveNodes(head.next);if(head.next !=null&& head.val < head.next.val){return head.next;}return head;}}
/**
* Definition for singly-linked list.
* type ListNode struct {
* Val int
* Next *ListNode
* }
*/funcremoveNodes(head *ListNode)*ListNode {if head ==nil{returnnil}
head.Next =removeNodes(head.Next)if head.Next !=nil&& head.Val < head.Next.Val {return head.Next
}return head
}
/**
* Example:
* var li = ListNode(5)
* var v = li.`val`
* Definition for singly-linked list.
* class ListNode(var `val`: Int) {
* var next: ListNode? = null
* }
*/class Solution {funremoveNodes(head: ListNode?): ListNode?{if(head ==null)returnnull
head.next =removeNodes(head.next)if(head.next !=null&& head.`val` < head.next!!.`val`){return head.next
}return head
}}
/**
* Definition for singly-linked list.
* public class ListNode {
* public var val: Int
* public var next: ListNode?
* public init() { self.val = 0; self.next = nil; }
* public init(_ val: Int) { self.val = val; self.next = nil; }
* public init(_ val: Int, _ next: ListNode?) { self.val = val; self.next = next; }
* }
*/classSolution{funcremoveNodes(_ head:ListNode?)->ListNode?{guardlet head = head else{returnnil}
head.next =removeNodes(head.next)iflet next = head.next, head.val < next.val {return head.next
}return head
}}
Time & Space Complexity
Time complexity: O(n)
Space complexity: O(n) for recursion stack.
4. Reverse Twice
Intuition
Finding the maximum to the right is hard when traversing left to right, but finding the maximum to the left is easy. By reversing the list first, the problem transforms: now we just need to keep nodes that are greater than or equal to all previous nodes. We traverse once while tracking the running maximum and remove smaller nodes. Finally, we reverse again to restore the original order.
Algorithm
Reverse the linked list.
Traverse the reversed list, tracking the maximum value seen so far (cur_max):
If cur.next.val < cur_max, skip it by setting cur.next = cur.next.next.
Otherwise, update cur_max and move to the next node.
# Definition for singly-linked list.# class ListNode:# def __init__(self, val=0, next=None):# self.val = val# self.next = nextclassSolution:defremoveNodes(self, head: Optional[ListNode])-> Optional[ListNode]:defreverse(head):
prev, cur =None, head
while cur:
tmp = cur.next
cur.next= prev
prev, cur = cur, tmp
return prev
head = reverse(head)
cur = head
cur_max = head.val
while cur and cur.next:if cur.next.val < cur_max:
cur.next= cur.next.nextelse:
cur_max = cur.next.val
cur = cur.nextreturn reverse(head)
/**
* 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; }
* }
*/publicclassSolution{publicListNoderemoveNodes(ListNode head){
head =reverse(head);ListNode cur = head;int curMax = head.val;while(cur !=null&& cur.next !=null){if(cur.next.val < curMax){
cur.next = cur.next.next;}else{
curMax = cur.next.val;
cur = cur.next;}}returnreverse(head);}privateListNodereverse(ListNode head){ListNode prev =null, cur = head;while(cur !=null){ListNode tmp = cur.next;
cur.next = prev;
prev = cur;
cur = tmp;}return prev;}}
/**
* Definition for singly-linked list.
* class ListNode {
* constructor(val = 0, next = null) {
* this.val = val;
* this.next = next;
* }
* }
*/classSolution{/**
* @param {ListNode} head
* @return {ListNode}
*/removeNodes(head){constreverse=(head)=>{let prev =null,
cur = head;while(cur){let tmp = cur.next;
cur.next = prev;
prev = cur;
cur = tmp;}return prev;};
head =reverse(head);let cur = head;let cur_max = head.val;while(cur && cur.next){if(cur.next.val < cur_max){
cur.next = cur.next.next;}else{
cur_max = cur.next.val;
cur = cur.next;}}returnreverse(head);}}
/**
* Definition for singly-linked list.
* public class ListNode {
* public int val;
* public ListNode next;
* public ListNode(int val=0, ListNode next=null) {
* this.val = val;
* this.next = next;
* }
* }
*/publicclassSolution{privateListNodeReverse(ListNode head){ListNode prev =null, cur = head;while(cur !=null){ListNode tmp = cur.next;
cur.next = prev;
prev = cur;
cur = tmp;}return prev;}publicListNodeRemoveNodes(ListNode head){
head =Reverse(head);ListNode cur = head;int curMax = head.val;while(cur !=null&& cur.next !=null){if(cur.next.val < curMax){
cur.next = cur.next.next;}else{
curMax = cur.next.val;
cur = cur.next;}}returnReverse(head);}}
/**
* Definition for singly-linked list.
* type ListNode struct {
* Val int
* Next *ListNode
* }
*/funcremoveNodes(head *ListNode)*ListNode {
reverse :=func(head *ListNode)*ListNode {var prev *ListNode =nil
cur := head
for cur !=nil{
tmp := cur.Next
cur.Next = prev
prev = cur
cur = tmp
}return prev
}
head =reverse(head)
cur := head
curMax := head.Val
for cur !=nil&& cur.Next !=nil{if cur.Next.Val < curMax {
cur.Next = cur.Next.Next
}else{
curMax = cur.Next.Val
cur = cur.Next
}}returnreverse(head)}
/**
* Example:
* var li = ListNode(5)
* var v = li.`val`
* Definition for singly-linked list.
* class ListNode(var `val`: Int) {
* var next: ListNode? = null
* }
*/class Solution {privatefunreverse(head: ListNode?): ListNode?{var prev: ListNode?=nullvar cur = head
while(cur !=null){val tmp = cur.next
cur.next = prev
prev = cur
cur = tmp
}return prev
}funremoveNodes(head: ListNode?): ListNode?{var h =reverse(head)var cur = h
var curMax = h!!.`val`
while(cur !=null&& cur.next !=null){if(cur.next!!.`val` < curMax){
cur.next = cur.next!!.next
}else{
curMax = cur.next!!.`val`
cur = cur.next
}}returnreverse(h)}}
/**
* Definition for singly-linked list.
* public class ListNode {
* public var val: Int
* public var next: ListNode?
* public init() { self.val = 0; self.next = nil; }
* public init(_ val: Int) { self.val = val; self.next = nil; }
* public init(_ val: Int, _ next: ListNode?) { self.val = val; self.next = next; }
* }
*/classSolution{funcreverse(_ head:ListNode?)->ListNode?{var prev:ListNode?=nilvar cur = head
while cur !=nil{let tmp = cur?.next
cur?.next = prev
prev = cur
cur = tmp
}return prev
}funcremoveNodes(_ head:ListNode?)->ListNode?{var head =reverse(head)var cur = head
var curMax = head!.val
while cur !=nil&& cur?.next !=nil{if cur!.next!.val < curMax {
cur?.next = cur?.next?.next
}else{
curMax = cur!.next!.val
cur = cur?.next
}}returnreverse(head)}}
Time & Space Complexity
Time complexity: O(n)
Space complexity: O(1) extra space.
Common Pitfalls
Comparing with Immediate Neighbor Instead of Maximum
The problem requires removing nodes that have any larger value to their right, not just an immediately adjacent larger value. A node should be kept only if it is greater than or equal to all nodes to its right. Using the wrong comparison leads to incorrect removals.
Incorrect Stack Maintenance
When using a monotonic stack, the stack must remain strictly decreasing. Forgetting to pop smaller elements before pushing, or using the wrong comparison operator (e.g., >= instead of >), breaks the invariant and produces wrong results.
Losing the New Head After Reversal
In the reverse-twice approach, both reversals change which node is the head. Failing to capture and return the correct head after the second reversal causes the function to return a pointer into the middle of the list or a stale reference.