Have you ever encountered a situation where you needed to sort an entire array, but you realized that only a small portion of the array was actually out of order? This is the essence of the "Smallest Subarray to be Sorted to make the whole array sorted" problem, a common and important challenge in the world of data structures and algorithms.
In this comprehensive article, we‘ll dive deep into this problem, exploring various approaches to solve it efficiently, and understanding the trade-offs between different solutions. By the end, you‘ll have a solid grasp of this problem and be equipped with the knowledge to tackle it in your own programming endeavors.
Understanding the Problem
The "Smallest Subarray to be Sorted to make the whole array sorted" problem can be stated as follows:
Given an unsorted array arr, find the subarray arr[s...e] such that sorting this subarray makes the whole array sorted.
In other words, we need to identify the smallest contiguous subarray that, when sorted, will result in the entire array being in sorted order.
Let‘s consider a few examples to better understand the problem:
Example 1:
Input: arr[] = [10, 12, 20, 30, 25, 40, 32, 31, 35, 50, 60]
Output: [3, 8]
Explanation: Sorting the subarray [25, 40, 32, 31, 35] makes the whole array sorted.Example 2:
Input: arr[] = [0, 1, 15, 25, 6, 7, 30, 40, 50]
Output: [2, 5]
Explanation: Sorting the subarray [15, 25, 6, 7] makes the whole array sorted.Example 3:
Input: arr[] = [30, 20, 10]
Output: [0, 2]
Explanation: We need to sort the whole array to make it sorted.In the first two examples, the input arrays are not completely sorted, and we need to identify the smallest subarray that, when sorted, will make the entire array sorted. In the third example, the array is already in descending order, so we need to sort the entire array to make it sorted.
Naive Approach: Using Sorting
One of the simplest approaches to solve this problem is to use sorting. The idea is to create an auxiliary array temp that is a copy of the original array arr, sort the temp array, and then compare the elements of the original array with the sorted temp array to find the leftmost and rightmost indices where the elements are not matching.
Here‘s the step-by-step implementation of this approach:
- Create a temporary array
tempthat is a copy of the original arrayarr. - Sort the
temparray using a sorting algorithm (e.g., quicksort, mergesort, etc.). - Iterate through the original array
arrfrom left to right and find the first indexswhere the element ofarris different from the corresponding element in the sortedtemparray. - Iterate through the original array
arrfrom right to left and find the first indexewhere the element ofarris different from the corresponding element in the sortedtemparray. - The subarray
arr[s...e]is the smallest subarray that needs to be sorted to make the entire array sorted.
Here‘s the implementation in various programming languages:
// C++ implementation
vector<int> printUnsorted(vector<int>& arr) {
int n = arr.size();
vector<int> temp = arr;
sort(temp.begin(), temp.end());
int s = 0, e = 0;
for (int i = 0; i < n; i++) {
if (arr[i] != temp[i]) {
s = i;
break;
}
}
for (int i = n - 1; i >= 0; i--) {
if (arr[i] != temp[i]) {
e = i;
break;
}
}
return {s, e};
}// Java implementation
static ArrayList<Integer> printUnsorted(int[] arr) {
int n = arr.length;
int[] temp = Arrays.copyOf(arr, n);
Arrays.sort(temp);
int s = 0, e = 0;
for (int i = 0; i < n; i++) {
if (arr[i] != temp[i]) {
s = i;
break;
}
}
for (int i = n - 1; i >= 0; i--) {
if (arr[i] != temp[i]) {
e = i;
break;
}
}
ArrayList<Integer> res = new ArrayList<>();
res.add(s);
res.add(e);
return res;
}# Python implementation
def printUnsorted(arr):
n = len(arr)
temp = arr[:]
temp.sort()
s = 0
e = 0
for i in range(n):
if arr[i] != temp[i]:
s = i
break
for i in range(n - 1, -1, -1):
if arr[i] != temp[i]:
e = i
break
return [s, e]The time complexity of this approach is O(n * log n) due to the sorting step, and the space complexity is O(n) due to the creation of the temporary array.
While this approach is straightforward and easy to understand, it has a major drawback: it requires creating an additional array and sorting it, which can be inefficient for large input sizes. In the next section, we‘ll explore a more efficient approach using a stack-based solution.
Better Approach: Using Stack
The idea behind this approach is to use a stack to keep track of the indices of the elements and find the nearest smaller element to the left of each element. This information can be used to determine the leftmost and rightmost indices of the subarray that needs to be sorted.
Here‘s the step-by-step implementation of this approach:
- Initialize variables
leftandrightto store the leftmost and rightmost indices of the subarray that needs to be sorted. Setleftton + 1andrightto-1. - Initialize a variable
maxito store the maximum element seen so far. - Use a stack to store the indices of the elements.
- Iterate through the array from left to right:
- While the current element is less than the element at the top of the stack, pop the stack.
- If the stack is not empty, update the
leftandrightindices based on the following conditions:- If the distance between the current index and the index at the top of the stack is greater than 1, update
leftto the minimum of the currentleftand the index at the top of the stack, and updaterightto the current index. - If the distance between the current index and the index at the top of the stack is 1 or less, check if the current element is less than the maximum element seen so far. If so, update
rightto the current index. If the element at the top of the stack is not the maximum element, updaterightto the previous index.
- If the distance between the current index and the index at the top of the stack is greater than 1, update
- If the stack is empty and the current index is not the first index, set
leftto-1andrightto the current index. - Update the
maxivariable to the maximum of the currentmaxiand the current element. - Push the current index onto the stack.
- Return the
leftandrightindices.
Here‘s the implementation in various programming languages:
// C++ implementation
vector<int> printUnsorted(vector<int>& arr) {
int n = arr.size();
int left = n + 1, right = -1;
int maxi = INT_MIN;
stack<int> st;
for (int i = 0; i < n; ++i) {
while (!st.empty() && arr[i] < arr[st.top()])
st.pop();
if (!st.empty()) {
if (i - st.top() > 1) {
left = min(left, st.top());
right = i;
} else {
if (arr[i] < maxi)
right = i;
else if (arr[st.top()] != maxi)
right = i - 1;
}
} else if (i != 0) {
left = -1;
right = i;
}
maxi = max(maxi, arr[i]);
st.push(i);
}
return {left + 1, right};
}// Java implementation
static ArrayList<Integer> printUnsorted(int[] arr) {
int n = arr.length;
int left = n + 1, right = -1;
int maxi = Integer.MIN_VALUE;
Stack<Integer> st = new Stack<>();
for (int i = 0; i < n; ++i) {
while (!st.empty() && arr[i] < arr[st.peek()])
st.pop();
if (!st.empty()) {
if (i - st.peek() > 1) {
left = Math.min(left, st.peek());
right = i;
} else {
if (arr[i] < maxi)
right = i;
else if (arr[st.peek()] != maxi)
right = i - 1;
}
} else if (i != 0) {
left = -1;
right = i;
}
maxi = Math.max(maxi, arr[i]);
st.push(i);
}
ArrayList<Integer> res = new ArrayList<>();
res.add(left + 1);
res.add(right);
return res;
}# Python implementation
def printUnsorted(arr):
n = len(arr)
left = n + 1
right = -1
maxi = -float(‘inf‘)
st = []
for i in range(n):
while st and arr[i] < arr[st[-1]]:
st.pop()
if st:
if i - st[-1] > 1:
left = min(left, st[-1])
right = i
else:
if arr[i] < maxi:
right = i
elif arr[st[-1]] != maxi:
right = i - 1
elif i != 0:
left = -1
right = i
maxi = max(maxi, arr[i])
st.append(i)
return [left + 1, right]The time complexity of this approach is O(n), and the space complexity is O(n) due to the use of the stack.
This approach is more efficient than the naive approach, as it doesn‘t require sorting the entire array. Instead, it uses the stack to keep track of the indices of the elements and determines the leftmost and rightmost indices of the subarray that needs to be sorted.
Expected Approach: O(n) Time and O(1) Space
The expected approach to solve this problem is to find the leftmost and rightmost indices of the subarray that needs to be sorted, and then find the minimum and maximum elements in that subarray. Finally, we need to find the first element in the left part of the array that is greater than the minimum element, and the last element in the right part of the array that is smaller than the maximum element.
Here‘s the step-by-step implementation of this approach:
- Find the leftmost and rightmost indices of the subarray that needs to be sorted:
- Scan the array from left to right and find the first element that is greater than the next element. This index is the leftmost index of the subarray.
- Scan the array from right to left and find the first element that is smaller than the next element. This index is the rightmost index of the subarray.
- If the array is already sorted, return
[0, 0]. - Find the minimum and maximum elements in the subarray
arr[left...right]. - Find the first element in
arr[0...left-1]that is greater than the minimum element, and update theleftindex accordingly. - Find the last element in
arr[right+1...n-1]that is smaller than the maximum element, and update therightindex accordingly. - Return the
leftandrightindices.
Here‘s the implementation in various programming languages:
// C++ implementation
vector<int> printUnsorted(vector<int>& arr) {
int n = arr.size();
int left = n + 1, right = -1;
for (int i = 0; i < n - 1; i++) {
if (arr[i] > arr[i + 1]) {
left = i;
break;
}
}
if (left == n + 1) {
return {0, 0};
}
for (int i = n - 1; i > 0; i--) {
if (arr[i] < arr[i - 1]) {
right = i;
break;
}
}
int maxi = arr[left], mini = arr[left];
for (int i = left + 1; i <= right; i++) {
maxi = max(maxi, arr[i]);
mini = min(mini, arr[i]);
}
for (int i = 0; i < left; i++) {
if (arr[i] > mini) {
left = i;
break;
}
}
for (int i = n - 1; i > right; i--) {
if (arr[i] < maxi) {
right = i;
break;
}
}
return {left, right};
}
// Java implementation
static ArrayList<Integer> printUnsorted(int[] arr) {
int n = arr.length;
int left = n + 1, right = -1;
for (int i = 0; i < n - 1; i++) {
if (arr[i] > arr[i + 1]) {
left = i;
break;
}
}
if (left == n + 1) {
return new ArrayList<>(Arrays.asList(0, 0));
}
for (int i = n - 1; i > 0; i--) {
if (arr[i] < arr[i - 1]) {
right = i;
break;
}
}
int maxi = arr[left], mini = arr[left];
for (int i = left + 1; i <= right; i++) {
maxi = Math.max(maxi, arr[i]);
mini =