Taro Logo

Find the Minimum and Maximum Number of Nodes Between Critical Points

Medium
Google logo
Google
1 view
Topics:
Linked Lists

A critical point in a linked list is defined as either a local maxima or a local minima.

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.

Note that a node can only be a local maxima/minima if there exists both a previous node and a next node.

Given a linked list head, return an array of length 2 containing [minDistance, maxDistance] where 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, return [-1, -1].

Example 1:

head = [3,1]

Output: [-1,-1]

Example 2:

head = [5,3,1,2,5,1,2]

Output: [1,3]

Example 3:

head = [1,3,2,2,3,2,2,2,7]

Output: [3,3]

Solution


Naive Solution

The most straightforward approach is to iterate through the linked list, identify all critical points, and then calculate the minimum and maximum distances between them. This involves:

  1. Traversing the linked list to store the values in an array.
  2. Iterating through the array to find all critical points and their indices.
  3. Calculating the minimum and maximum distances between these critical points.

Code Example (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]

    arr = []
    while head:
        arr.append(head.val)
        head = head.next

    critical_indices = []
    for i in range(1, len(arr) - 1):
        if arr[i-1] < arr[i] > arr[i+1] or arr[i-1] > arr[i] < arr[i+1]:
            critical_indices.append(i)

    if len(critical_indices) < 2:
        return [-1, -1]

    min_dist = float('inf')
    max_dist = 0
    for i in range(len(critical_indices)):
        for j in range(i + 1, len(critical_indices)):
            dist = abs(critical_indices[i] - critical_indices[j])
            min_dist = min(min_dist, dist)
            max_dist = max(max_dist, dist)

    return [min_dist, max_dist]

Big O Analysis:

  • Time Complexity: O(N + K^2), where N is the number of nodes in the linked list and K is the number of critical points. In the worst case, K can be close to N, leading to O(N^2).
  • Space Complexity: O(N) to store the linked list values in an array.

Optimal Solution

A more efficient approach involves traversing the linked list only once, identifying critical points on the fly, and keeping track of the minimum and maximum distances.

  1. Initialize first_critical and last_critical to -1.
  2. Iterate through the linked list, keeping track of the previous, current, and next node values.
  3. When a critical point is found:
    • If it's the first critical point, store its index.
    • Update the minimum and maximum distances based on the previous critical point's index.
  4. After traversal, return the minimum and maximum distances.

Code Example (Python):

class ListNode:
    def __init__(self, val=0, next=None):
        self.val = val
        self.next = next

def critical_points_optimal(head):
    if not head or not head.next or not head.next.next:
        return [-1, -1]

    prev = head
    curr = head.next
    next_node = head.next.next
    index = 1
    first_critical = -1
    last_critical = -1
    min_dist = float('inf')
    max_dist = 0

    while next_node:
        if prev.val < curr.val > next_node.val or prev.val > curr.val < next_node.val:
            if first_critical == -1:
                first_critical = index
                last_critical = index
            else:
                min_dist = min(min_dist, index - last_critical)
                max_dist = index - first_critical
                last_critical = index

        prev = curr
        curr = next_node
        next_node = next_node.next if next_node.next else None
        index += 1

    if min_dist == float('inf'):
        return [-1, -1]
    else:
        return [min_dist, max_dist]

Big O Analysis:

  • Time Complexity: O(N), where N is the number of nodes in the linked list, as we traverse the list once.
  • Space Complexity: O(1), as we only use a constant amount of extra space.

Edge Cases

  • Empty List or List with fewer than 3 nodes: In these cases, no critical points can exist, so return [-1, -1].
  • No Critical Points: If no critical points are found during traversal, return [-1, -1].
  • Only One Critical Point: If only one critical point is found, a minimum and maximum distance cannot be calculated, so return [-1, -1].
  • Adjacent Critical Points: The minimum distance between critical points can be 1 if they are adjacent nodes.

Summary

The optimal solution offers significant efficiency by traversing the linked list only once and using constant extra space. The naive solution, while easier to understand, suffers from a higher time complexity due to the need to store linked list values and perform nested iterations for calculating distances.