{x}
blog image

Shortest Subarray to be Removed to Make Array Sorted

Given an integer array arr, remove a subarray (can be empty) from arr such that the remaining elements in arr are non-decreasing.

Return the length of the shortest subarray to remove.

A subarray is a contiguous subsequence of the array.

 

Example 1:

Input: arr = [1,2,3,10,4,2,3,5]
Output: 3
Explanation: The shortest subarray we can remove is [10,4,2] of length 3. The remaining elements after that will be [1,2,3,3,5] which are sorted.
Another correct solution is to remove the subarray [3,10,4].

Example 2:

Input: arr = [5,4,3,2,1]
Output: 4
Explanation: Since the array is strictly decreasing, we can only keep a single element. Therefore we need to remove a subarray of length 4, either [5,4,3,2] or [4,3,2,1].

Example 3:

Input: arr = [1,2,3]
Output: 0
Explanation: The array is already non-decreasing. We do not need to remove any elements.

 

Constraints:

  • 1 <= arr.length <= 105
  • 0 <= arr[i] <= 109

Solution Explanation for Shortest Subarray to be Removed to Make Array Sorted

This problem asks to find the shortest subarray to remove from an integer array to make the remaining array non-decreasing. We'll explore two solutions: one using binary search and another using a two-pointer approach. Both solutions leverage the observation that we only need to consider removing a subarray that disrupts the non-decreasing order.

Core Idea:

The solutions efficiently identify the longest non-decreasing prefix and suffix of the input array. The subarray to be removed lies between these two portions. We then iterate through possibilities, minimizing the length of the subarray removed.

Solution 1: Two Pointers + Binary Search

  1. Find Longest Non-decreasing Prefix and Suffix:

    • We use two pointers, i and j, to find the indices marking the end of the longest non-decreasing prefix and the beginning of the longest non-decreasing suffix, respectively.
  2. Handle Trivial Cases:

    • If i >= j, the array is already non-decreasing, and we return 0.
  3. Initial Answer:

    • The minimum length of the subarray to remove is initially set to the minimum of the length of the remaining array after the prefix (n - i - 1) and the length of the remaining array before the suffix (j).
  4. Iterate and Refine:

    • The code iterates through the prefix (l from 0 to i). For each l, we use binary search (bisect_left in Python, lower_bound in C++, sort.SearchInts in Go, search in Java) to find the smallest index r in the suffix such that arr[r] >= arr[l]. This efficiently finds the rightmost index that maintains the non-decreasing property when removing arr[l+1...r-1].
    • The length of the removed subarray is r - l - 1, and we update the answer (ans) if this length is smaller.

Time Complexity Analysis (Solution 1):

  • Finding the prefix and suffix takes O(n) time.
  • The outer loop iterates O(i) times, which is O(n) in the worst case.
  • Binary search within the loop takes O(log n) time.
  • Overall, the time complexity is O(n log n).

Space Complexity Analysis (Solution 1):

  • The algorithm uses constant extra space, so the space complexity is O(1).

Solution 2: Two Pointers (Optimized)

This solution improves upon the previous one by replacing binary search with a linear scan using two pointers.

  1. Steps 1-3: Same as Solution 1.

  2. Linear Scan for Optimal Removal:

    • The code iterates through the prefix (l from 0 to i). For each l, a second pointer r starts at j. It moves to the right until it finds an element arr[r] that is greater than or equal to arr[l]. This avoids the logarithmic complexity of binary search.

Time Complexity Analysis (Solution 2):

  • Finding the prefix and suffix: O(n)
  • Outer loop: O(n) in the worst case
  • Inner loop (linear scan): O(n) in the worst case (although often much less)
  • Overall time complexity: O(n^2) in the worst-case scenario, but it often performs much better in practice because the inner loop doesn't always traverse the entire suffix.

Space Complexity Analysis (Solution 2):

  • O(1) as it uses constant extra space.

Code Examples (Python - Solution 2):

class Solution:
    def findLengthOfShortestSubarray(self, arr: List[int]) -> int:
        n = len(arr)
        i, j = 0, n - 1
        while i + 1 < n and arr[i] <= arr[i + 1]:
            i += 1
        while j - 1 >= 0 and arr[j - 1] <= arr[j]:
            j -= 1
        if i >= j:
            return 0
        ans = min(n - i - 1, j)
        r = j
        for l in range(i + 1):
            while r < n and arr[r] < arr[l]:  # Linear scan instead of binary search
                r += 1
            ans = min(ans, r - l - 1)
        return ans

The other languages' code would follow similar logic. Remember to adjust the binary search or linear scan according to the chosen language's standard library functions. Solution 2 is generally faster in practice, despite its theoretically worse worst-case time complexity. Solution 1 provides a guaranteed O(n log n) time complexity, making it slightly more predictable in terms of performance. The choice depends on the prioritization of theoretical vs. practical efficiency.