Taro Logo

Find the Minimum and Maximum Number of Nodes Between Critical Points

Medium
Amazon logo
Amazon
2 views
Topics:
Linked Lists

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:

  • A node is a local maxima if the current node has a value strictly greater than the previous node and the next node.
  • A node is a local minima if the current node has a value strictly smaller than the previous node and the next node.

Important considerations:

  • A node can only be a local maxima/minima if there exists both a previous node and a next node. This implies that the head and tail nodes cannot be critical points.

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?

Solution


Naive Solution

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.

Optimal Solution

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:

  1. Initialize first_critical_point and last_critical_point to -1. These will store the index of the first and last critical points encountered.
  2. Initialize min_distance to infinity and max_distance to 0.
  3. Iterate through the linked list, starting from the second node.
  4. For each node, check if it's a critical point.
  5. If it's the first critical point encountered, update first_critical_point and last_critical_point.
  6. If it's not the first critical point, calculate the distance between the current critical point and the previous one (last_critical_point). Update min_distance and max_distance accordingly. Then, update last_critical_point to the current critical point.
  7. After the iteration, if fewer than two critical points were found, return [-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.

Edge Cases

  • Empty list or list with only one node: Return [-1, -1] because there are no critical points.
  • List with two nodes: Return [-1, -1] because a critical point requires a previous and next node.
  • No critical points: Return [-1, -1] if no local minima or maxima are found.
  • Only one critical point: Return [-1, -1] because we need at least two critical points to calculate distances.
  • Critical points are adjacent: Ensure the minimum distance calculation correctly handles this scenario.