Let's explore a linked list problem focused on identifying specific nodes based on their relationship with neighboring nodes. A critical point in a linked list is defined as either a local maxima or a local minima. Here are the specific definitions:
Important considerations:
Given the head
of a linked list, your task is to return an array of length 2, [minDistance, maxDistance]
. minDistance
is the minimum distance between any two distinct critical points, and maxDistance
is the maximum distance between any two distinct critical points. If there are fewer than two critical points in the linked list, return [-1, -1]
. The distance between two nodes is simply the number of nodes between them. Let's solidify this with some examples:
Example 1:
Consider the linked list [5, 3, 1, 2, 5, 1, 2]
. The critical points are:
1
(at index 2, using 0-based indexing), as it is smaller than both 3
and 2
.5
(at index 4), as it is greater than both 2
and 1
.1
(at index 5), as it is smaller than both 5
and 2
.The minimum distance is between the critical points at indices 4 and 5, which is 1
. The maximum distance is between the critical points at indices 2 and 5, which is 3
. Therefore, the output should be [1, 3]
.
Example 2:
Consider the linked list [1, 3, 2, 2, 3, 2, 2, 2, 7]
. The critical points are:
3
(at index 1), as it is greater than both 1
and 2
.3
(at index 4), as it is greater than both 2
and 2
.The minimum and maximum distance is between the critical points at indices 1 and 4, which is 3
. Therefore, the output should be [3, 3]
.
How would you approach this problem efficiently, keeping in mind space and time complexities?
The most straightforward approach is to iterate through the linked list, identify critical points, store their indices, and then calculate the minimum and maximum distances between these critical points. This involves a linear scan to find the critical points and then a nested loop to compare the distances between all pairs of critical points.
Code (Python):
class ListNode:
def __init__(self, val=0, next=None):
self.val = val
self.next = next
def critical_points(head):
if not head or not head.next:
return [-1, -1]
nodes = []
while head:
nodes.append(head.val)
head = head.next
n = len(nodes)
critical_pts = []
for i in range(1, n - 1):
if nodes[i] > nodes[i - 1] and nodes[i] > nodes[i + 1]:
critical_pts.append(i + 1)
elif nodes[i] < nodes[i - 1] and nodes[i] < nodes[i + 1]:
critical_pts.append(i + 1)
if len(critical_pts) < 2:
return [-1, -1]
min_dist = float('inf')
max_dist = 0
for i in range(len(critical_pts)):
for j in range(i + 1, len(critical_pts)):
dist = critical_pts[j] - critical_pts[i]
min_dist = min(min_dist, dist)
max_dist = max(max_dist, dist)
return [min_dist, max_dist]
Time Complexity: O(N^2), where N is the number of nodes in the linked list. This is because we have a nested loop to calculate the distances between all pairs of critical points.
Space Complexity: O(N), where N is the number of nodes in the linked list. This is used to store the values of all the nodes and the critical point indices.
A more efficient approach involves iterating through the linked list only once. During this iteration, we identify critical points and simultaneously keep track of the minimum and maximum distances between them. This eliminates the need for nested loops.
Algorithm:
first_critical_point
and last_critical_point
to -1
. These will store the index of the first and last critical points encountered.min_distance
to infinity and max_distance
to 0.first_critical_point
and last_critical_point
.last_critical_point
). Update min_distance
and max_distance
accordingly. Then, update last_critical_point
to the current critical point.[-1, -1]
. Otherwise, return [min_distance, max_distance]
.Code (Python):
class ListNode:
def __init__(self, val=0, next=None):
self.val = val
self.next = next
def critical_points(head):
if not head or not head.next or not head.next.next:
return [-1, -1]
first_critical_point = -1
last_critical_point = -1
min_distance = float('inf')
max_distance = 0
index = 1
prev = head
curr = head.next
while curr.next:
next_node = curr.next
if curr.val > prev.val and curr.val > next_node.val:
# Local maxima
if first_critical_point == -1:
first_critical_point = index + 1
last_critical_point = index + 1
else:
min_distance = min(min_distance, (index + 1) - last_critical_point)
max_distance = (index + 1) - first_critical_point
last_critical_point = index + 1
elif curr.val < prev.val and curr.val < next_node.val:
# Local minima
if first_critical_point == -1:
first_critical_point = index + 1
last_critical_point = index + 1
else:
min_distance = min(min_distance, (index + 1) - last_critical_point)
max_distance = (index + 1) - first_critical_point
last_critical_point = index + 1
prev = curr
curr = next_node
index += 1
if first_critical_point == last_critical_point:
return [-1, -1]
return [min_distance, max_distance]
Time Complexity: O(N), where N is the number of nodes in the linked list, as we iterate through the list only once.
Space Complexity: O(1), as we only use a constant amount of extra space.
[-1, -1]
because there are no critical points.[-1, -1]
because a critical point requires a previous and next node.[-1, -1]
if no local minima or maxima are found.[-1, -1]
because we need at least two critical points to calculate distances.