Problem Statement
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 thehead
of the modified linked list.
Examples
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, 10^5]
.1 <= Node.val <= 10^5
Intuition
Understanding the problem
We are given head
of a singly LinkedList, we have remove every node which has a node with a greater value anywhere to the right side of it.
The problem we have to solve is, how to keep a track of all the nodes which are greater or smaller than the current node. Lets see how we can tackle this problem.
Approach
Using Monotonic Stack
If we think about solving this problem using a data-structure, we can use a monotonic stack. You can read more about Monotonic stacks here:
Read about Monotonic Stack here
By using a monotonic stack, we can ensure that while traversing the LinkedList, we will always have values that are greater than the values on their left side. You can see the dry run of the same below:
At every iteration, we continue pushing values to the stack. When we encounter a greater value, we pop elements until we find a value larger than the current one.
Using Reverse and Filter
We reverse the LinkedList, then we traverse the list, keeping nodes with values greater than or equal to the previous node's value. After finishing the traversal, we reverse the list again to restore the original order.
Solution
Using Monotonic Stack
Python code:
# Definition for singly-linked list.
# class ListNode:
# def __init__(self, val=0, next=None):
# self.val = val
# self.next = next
class Solution:
def removeNodes(self, head: Optional[ListNode]) -> Optional[ListNode]:
# initialize the stack
stack=[]
# duplicate head to a local variable
current_node=head
# loop till end of linked list
while current_node:
# if there is value i stack
# and last value of stack is less than current node's value
while stack and stack[-1].val<current_node.val:
# pop from the stack
# as these nodes have nodes with greater values on their right side
stack.pop()
# append next value to stack
stack.append(current_node)
# move to next node
current_node=current_node.next
# local variable to reconnect nodes in stack
next_node=None
# reconnect the nodes in stack
while stack:
# get node from stack
current_node=stack.pop()
# make current_node's next to next_node's next
current_node.next=next_node
# make current_node the next_node
next_node=current_node
# return the reconnected list
return current_node
Using Reverse and Filter
# Definition for singly-linked list.
# class ListNode:
# def __init__(self, val=0, next=None):
# self.val = val
# self.next = next
class Solution:
def removeNodes(self, head: Optional[ListNode]) -> Optional[ListNode]:
# check if linkedlist is empty or has only one node
if not head or not head.next:
return head
# Reverse the linkedlist
prev_node,curr_node=None,head
while curr_node:
curr_node.next,prev_node,curr_node=prev_node,curr_node,curr_node.next
# make curr_node equal to prev_node.next
# and prev_node.next equal to None
curr_node, prev_node.next = prev_node.next, None
# iterate till curr_node is not None
while curr_node:
# initialize a temp to remember next of curr_node
temp = curr_node.next
# if curr_node's value is greater than prev_node's val
# tehn remove the node and reverse
if curr_node.val >= prev_node.val:
curr_node.next = prev_node
prev_node = curr_node
# make curr_node equal to temp(next of curr_node)
curr_node = temp
# return prev_node as this is the new head
return prev_node
Time and Space complexity
Using Monotonic Stack
Time Complexity:
$$O(n)$$
Since we're iterating over the LinkedList at least once.
Space Complexity:
$$O(n)$$
Since we're using a stack and in worst case it will have same size as input linked list.
Using Reverse and Filter
Time Complexity:
$$O(n)$$
Since we're iterating over the LinkedList at least once.
Space Complexity:
$$O(1)$$
As we're not allocating any extra space in this approach
Conclusion
In this problem we get to learn two very good concepts:
Using a monotonic stack
Using Reverse and Filter
Hope that explanation made sense and you found this blog helpful. If you've made it this far, please give it a like and drop a comment.
Happy Coding!๐