2058. Find the Minimum and Maximum Number of Nodes Between Critical Points

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:

Input: head = [3,1]
Output: [-1,-1]
Explanation: There are no critical points in [3,1].

Example 2:

Input: head = [5,3,1,2,5,1,2]
Output: [1,3]
Explanation: There are three critical points:
- [5,3,1,2,5,1,2]: The third node is a local minima because 1 is less than 3 and 2.
- [5,3,1,2,5,1,2]: The fifth node is a local maxima because 5 is greater than 2 and 1.
- [5,3,1,2,5,1,2]: The sixth node is a local minima because 1 is less than 5 and 2.
The minimum distance is between the fifth and the sixth node. minDistance = 6 - 5 = 1.
The maximum distance is between the third and the sixth node. maxDistance = 6 - 3 = 3.

Example 3:

Input: head = [1,3,2,2,3,2,2,2,7]
Output: [3,3]
Explanation: There are two critical points:
- [1,3,2,2,3,2,2,2,7]: The second node is a local maxima because 3 is greater than 1 and 2.
- [1,3,2,2,3,2,2,2,7]: The fifth node is a local maxima because 3 is greater than 2 and 2.
Both the minimum and maximum distances are between the second and the fifth node.
Thus, minDistance and maxDistance is 5 - 2 = 3.
Note that the last node is not considered a local maxima because it does not have a next node.

Constraints:

  • The number of nodes in the list is in the range [2, 105].
  • 1 <= Node.val <= 105

Approach 01:

#include <vector>
#include <limits>
using namespace std;

class Solution {
public:
    vector<int> nodesBetweenCriticalPoints(ListNode* head) {
        int min_distance = numeric_limits<int>::max();
        int first_critical_index = -1;
        int prev_critical_index = -1;
        int index = 1;
        ListNode* prev_node = head;        // Pointer to the current node.
        ListNode* curr_node = head->next;  // Pointer to the next node.

        while (curr_node->next) {
            // Check for critical points (local maxima or minima).
            if ((curr_node->val > prev_node->val && curr_node->val > curr_node->next->val) ||
                (curr_node->val < prev_node->val && curr_node->val < curr_node->next->val)) {
                
                // First critical index (only set once).
                if (first_critical_index == -1) {
                    first_critical_index = index;
                }
                
                // Calculate minimum distance between consecutive critical points.
                if (prev_critical_index != -1) {
                    min_distance = min(min_distance, index - prev_critical_index);
                }
                
                prev_critical_index = index;
            }
            
            prev_node = curr_node;
            curr_node = curr_node->next;
            ++index;
        }

        if (min_distance == numeric_limits<int>::max()) {
            return {-1, -1};  // No critical points found.
        }
        
        // Calculate distance between the first and last critical points.
        int max_distance = prev_critical_index - first_critical_index;
        
        return {min_distance, max_distance};
    }
};

// Helper function to create a linked list
ListNode* create_list(vector<int>& values) {
    ListNode* dummy = new ListNode(0);
    ListNode* current = dummy;
    for (int val : values) {
        current->next = new ListNode(val);
        current = current->next;
    }
    return dummy->next;
}
class Solution:
    def nodesBetweenCriticalPoints(self, head: Optional[ListNode]) -> List[int]:
        import sys
        min_distance = sys.maxsize
        first_critical_index = -1
        prev_critical_index = -1
        index = 1
        prev_node = head  # Pointer to the current node.
        curr_node = head.next  # Pointer to the next node.

        while curr_node and curr_node.next:
            # Check for critical points (local maxima or minima).
            if (curr_node.val > prev_node.val and curr_node.val > curr_node.next.val) or \
               (curr_node.val < prev_node.val and curr_node.val < curr_node.next.val):
                
                # First critical index (only set once).
                if first_critical_index == -1:
                    first_critical_index = index
                
                # Calculate minimum distance between consecutive critical points.
                if prev_critical_index != -1:
                    min_distance = min(min_distance, index - prev_critical_index)
                
                prev_critical_index = index
            
            prev_node = curr_node
            curr_node = curr_node.next
            index += 1

        if min_distance == sys.maxsize:
            return [-1, -1]  # No critical points found.
        
        # Calculate distance between the first and last critical points.
        max_distance = prev_critical_index - first_critical_index
        
        return [min_distance, max_distance]

# Helper function to create a linked list
def create_list(values: List[int]) -> ListNode:
    dummy = ListNode(0)
    current = dummy
    for val in values:
        current.next = ListNode(val)
        current = current.next
    return dummy.next

Time Complexity

  • Traversal:

    Iterates through each node of the linked list once, so \( O(n) \), where \( n \) is the number of nodes.

  • Checking Critical Points:

    For each node (except the last one), checks conditions and updates variables in constant time, \( O(1) \) per node.

  • Sorting the Edges:

    Since there’s no sorting involved, this step doesn’t contribute to time complexity.

  • Overall Time Complexity:

    The dominant factor is the traversal of the linked list, \( O(n) \).

Space Complexity

  • Additional Space:

    Uses a few extra integer variables and pointers, \( O(1) \) additional space.

  • UnionFind Objects:

    Each UnionFind object uses \( O(n) \) space, and we have two such objects, so total space is \( O(n) \).

  • Edges Vector:

    The `edges` vector uses \( O(E) \) space, but it’s not applicable here.

  • Overall Space Complexity:

    The space complexity is dominated by the variables and pointers used, \( O(1) \).


Leave a Comment

Your email address will not be published. Required fields are marked *

Scroll to Top