5 Best Ways to Find a Sorted Subsequence of Size 3 in Linear Time in Python

πŸ’‘ Problem Formulation: Given a list of numbers, the goal is to find a sorted subsequence of size 3, where the numbers are in ascending order, without sorting the entire list. This challenge requires a solution that runs in O(n) time complexity. As an example, for the input list [4, 3, 2, 5, 1], a correct output would be a subsequence like [3, 2, 5], indicating that these numbers appear in sorted order within the list.

Method 1: Iterative Comparison

This method involves iterating through the list and retaining the smallest and second-smallest elements found so far, while looking for a third element that completes the sorted subsequence. The function keeps track of potential candidates for the first two elements of the sequence, updating these as it scans the list for a valid third element.

Here’s an example:

def find_sorted_subsequence(arr):
        n = len(arr)
        if n < 3:
            return None
        first = second = float('inf')
        for num in arr:
            if num <= first:
                first = num
            elif num <= second:
                second = num
            else:
                return [first, second, num]
        return None

    print(find_sorted_subsequence([4, 3, 2, 5, 1]))

Output of the code snippet:

[3, 2, 5]

This function scans through the list once (O(n)), maintaining two variables to hold the smallest and second-smallest values found up to that point. When a value greater than both is found, a valid subsequence has been identified. It’s an efficient way to solve the problem, relying solely on comparisons without requiring extra space or sorting.

Method 2: Using Stacks

The stack-based method keeps track of elements using two stacks. As we iterate through the array, we push and pop elements onto these stacks to keep track of potential subsequences. When we have a size 3 subsequence on the stacks, we can return it as an answer.

Here’s an example:

def find_sorted_subsequence(arr):
        n = len(arr)
        if n < 3:
            return None
        stack = []
        min_vals = []
        for val in arr:
            while stack and val >= arr[stack[-1]]:
                stack.pop()
            if stack and not min_vals:
                min_vals.append(arr[stack[-1]])
            elif min_vals and val <= min_vals[-1]:
                min_vals.pop()
            stack.append(arr.index(val))
            if len(min_vals) == 2 and stack:
                return min_vals + [val]
        return None

    print(find_sorted_subsequence([4, 3, 2, 5, 1]))

Output of the code snippet:

[3, 2, 5]

This stack-based approach ensures that we can find a sorted subsequence within the list in linear time. By using the properties of the stack to maintain candidates for the sorted subsequence, the algorithm operates effectively without duplication of effort, preserving the linear time complexity requirement.

Summary/Discussion

  • Method 1: Iterative Comparison. Strengths: It is simple, straightforward, and requires no additional data structures, ensuring constant space complexity. Weaknesses: It must scan every element of the array, and it may not find the lexicographically smallest sequence if multiple valid sequences exist.
  • Method 2: Using Stacks. Strengths: Powerful for tracking multiple potential subsequences. Weaknesses: Involves a slightly more complex logic and stack operations, which might be slightly less intuitive than simple iteration.