Shortest Unsorted Continuous Subarray
Problem
Given an integer array nums, you need to find one continuous subarray that if you only sort this subarray in ascending order, then the whole array will be sorted in ascending order.
Return the shortest such subarray and output its length.
OR
Give a list of unsorted number, find the min window or min sublist or min subarray of the input, such as if sublist is sorted, the whole list is sorted too.
Examples
Example 1:
Input: nums = [2,6,4,8,10,9,15]
Output: 5
Explanation: You need to sort [6, 4, 8, 10, 9] in ascending order to make the whole array sorted in ascending order.
Example 2:
Input: nums = [1,2,3,4]
Output: 0
Example 3:
Input: nums = [1]
Output: 0
Variant
Variant 1 - Return the bounds instead of length
Instead of returning right - left + 1, return (left, right) where left and right are the start and end indices of the unsorted window. If the array is already sorted, return (-1, -1) or null to indicate no sorting is needed.
Examples for Variant 1:
Input: nums = [2,6,4,8,10,9,15]
Output: (1, 5)
Explanation: Need to sort subarray from index 1 to 5: [6, 4, 8, 10, 9]
Input: nums = [1,2,3,4]
Output: (-1, -1)
Explanation: Array is already sorted
Solution
The idea is to assume left part and right part of array is somehow sorted. WE have to find hte unsorted array somewhere in the middle.
Method 1 - Sorting and Comparing
Intuition
If we sort the array, the sorted version will show us which elements are out of place. By comparing the original and sorted arrays, we can find the boundaries of the shortest subarray that needs sorting.
Approach
- Copy and sort the input array.
- Compare the original and sorted arrays from both ends to find the first and last indices where they differ.
- The subarray between these indices is the minimal window that must be sorted.
- If the array is already sorted, return 0.
Complexity
- ⏰ Time complexity:
O(n log n)— Sorting the array dominates. - 🧺 Space complexity:
O(n)— Copy of the array for sorting.
Code
Java
class Solution {
public int findUnsortedSubarray(int[] nums) {
int n = nums.length;
int[] sortedNums = Arrays.copyOf(nums, n);
Arrays.sort(sortedNums);
int start = 0;
while (start < n && nums[start] == sortedNums[start]) start++;
int end = n - 1;
while (end > start && nums[end] == sortedNums[end]) end--;
return end - start + 1;
}
}
Python
class Solution:
def findUnsortedSubarray(self, nums):
sorted_nums = sorted(nums)
start, end = 0, len(nums) - 1
while start < len(nums) and nums[start] == sorted_nums[start]:
start += 1
while end > start and nums[end] == sorted_nums[end]:
end -= 1
return end - start + 1
Method 2 - Greedy (No Sorting)
Intuition
Instead of sorting, we can scan from both ends to find the boundaries of the unsorted subarray. By tracking the maximum and minimum values while traversing, we can identify where the order breaks and thus the minimal window to sort.
Approach
- Traverse from left to right, tracking the running maximum. If an element is less than the max, update the right boundary.
- Traverse from right to left, tracking the running minimum. If an element is greater than the min, update the left boundary.
- The window between left and right is the shortest unsorted subarray.
- If the array is already sorted, return 0.
Complexity
- ⏰ Time complexity:
O(n)— Single pass from both ends. - 🧺 Space complexity:
O(1)— Only variables for boundaries and min/max.
Code
C++
class Solution {
public:
int findUnsortedSubarray(vector<int>& nums) {
int n = nums.size();
int left = n - 1, right = 0;
int max_num = nums[0], min_num = nums[n - 1];
for (int i = 0; i < n; ++i) {
max_num = max(max_num, nums[i]);
if (nums[i] < max_num) right = i;
}
for (int i = n - 1; i >= 0; --i) {
min_num = min(min_num, nums[i]);
if (nums[i] > min_num) left = i;
}
if (right <= left) return 0;
return right - left + 1;
}
};
Go
func findUnsortedSubarray(nums []int) int {
n := len(nums)
left, right := n-1, 0
maxNum, minNum := nums[0], nums[n-1]
for i := 0; i < n; i++ {
if nums[i] < maxNum {
right = i
}
if nums[i] > maxNum {
maxNum = nums[i]
}
}
for i := n-1; i >= 0; i-- {
if nums[i] > minNum {
left = i
}
if nums[i] < minNum {
minNum = nums[i]
}
}
if right <= left {
return 0
}
return right - left + 1
}
Java
class Solution {
public int findUnsortedSubarray(int[] nums) {
int n = nums.length;
int left = n - 1, right = 0;
int max = nums[0], min = nums[n - 1];
for (int i = 0; i < n; ++i) {
max = Math.max(max, nums[i]);
if (nums[i] < max) right = i;
}
for (int i = n - 1; i >= 0; --i) {
min = Math.min(min, nums[i]);
if (nums[i] > min) left = i;
}
if (right <= left) return 0;
return right - left + 1;
}
}
Kotlin
class Solution {
fun findUnsortedSubarray(nums: IntArray): Int {
val n = nums.size
var left = n - 1
var right = 0
var maxNum = nums[0]
var minNum = nums[n - 1]
for (i in 0 until n) {
maxNum = maxOf(maxNum, nums[i])
if (nums[i] < maxNum) right = i
}
for (i in n - 1 downTo 0) {
minNum = minOf(minNum, nums[i])
if (nums[i] > minNum) left = i
}
if (right <= left) return 0
return right - left + 1
}
}
Python
class Solution:
def findUnsortedSubarray(self, nums):
n = len(nums)
left, right = n - 1, 0
max_num, min_num = nums[0], nums[-1]
for i in range(n):
max_num = max(max_num, nums[i])
if nums[i] < max_num:
right = i
for i in range(n - 1, -1, -1):
min_num = min(min_num, nums[i])
if nums[i] > min_num:
left = i
if right <= left:
return 0
return right - left + 1
Rust
impl Solution {
pub fn find_unsorted_subarray(nums: Vec<i32>) -> i32 {
let n = nums.len();
let mut left = n - 1;
let mut right = 0;
let mut max_num = nums[0];
let mut min_num = nums[n - 1];
for i in 0..n {
max_num = max_num.max(nums[i]);
if nums[i] < max_num {
right = i;
}
}
for i in (0..n).rev() {
min_num = min_num.min(nums[i]);
if nums[i] > min_num {
left = i;
}
}
if right <= left {
return 0;
}
(right - left + 1) as i32
}
}
TypeScript
class Solution {
findUnsortedSubarray(nums: number[]): number {
const n = nums.length;
let left = n - 1, right = 0;
let maxNum = nums[0], minNum = nums[n - 1];
for (let i = 0; i < n; i++) {
maxNum = Math.max(maxNum, nums[i]);
if (nums[i] < maxNum) right = i;
}
for (let i = n - 1; i >= 0; i--) {
minNum = Math.min(minNum, nums[i]);
if (nums[i] > minNum) left = i;
}
if (right <= left) return 0;
return right - left + 1;
}
}
Solution for Variant 1 - Return Bounds
For the variant that returns the actual bounds instead of the length, we use the same core algorithm but modify the return value.
Method 1 - Greedy (Return Bounds)
Intuition
Same as the main problem - we find the boundaries of the unsorted subarray by tracking maximum from left and minimum from right. The only difference is we return the actual indices instead of the length.
Approach
- Use the same two-pass approach as Method 2 above
- Find left and right boundaries of the unsorted window
- Return
(left, right)if unsorted window exists, otherwise return(-1, -1)
Code
C++
class Solution {
public:
pair<int, int> findUnsortedSubarrayBounds(vector<int>& nums) {
int n = nums.size();
int left = n - 1, right = 0;
int max_num = nums[0], min_num = nums[n - 1];
for (int i = 0; i < n; ++i) {
max_num = max(max_num, nums[i]);
if (nums[i] < max_num) right = i;
}
for (int i = n - 1; i >= 0; --i) {
min_num = min(min_num, nums[i]);
if (nums[i] > min_num) left = i;
}
if (right <= left) return {-1, -1};
return {left, right};
}
};
Go
func findUnsortedSubarrayBounds(nums []int) []int {
n := len(nums)
left, right := n-1, 0
maxNum, minNum := nums[0], nums[n-1]
for i := 0; i < n; i++ {
if nums[i] < maxNum {
right = i
}
if nums[i] > maxNum {
maxNum = nums[i]
}
}
for i := n-1; i >= 0; i-- {
if nums[i] > minNum {
left = i
}
if nums[i] < minNum {
minNum = nums[i]
}
}
if right <= left {
return []int{-1, -1}
}
return []int{left, right}
}
Java
class Solution {
public int[] findUnsortedSubarrayBounds(int[] nums) {
int n = nums.length;
int left = n - 1, right = 0;
int max = nums[0], min = nums[n - 1];
for (int i = 0; i < n; ++i) {
max = Math.max(max, nums[i]);
if (nums[i] < max) right = i;
}
for (int i = n - 1; i >= 0; --i) {
min = Math.min(min, nums[i]);
if (nums[i] > min) left = i;
}
if (right <= left) return new int[]{-1, -1};
return new int[]{left, right};
}
}
Kotlin
class Solution {
fun findUnsortedSubarrayBounds(nums: IntArray): IntArray {
val n = nums.size
var left = n - 1
var right = 0
var maxNum = nums[0]
var minNum = nums[n - 1]
for (i in 0 until n) {
maxNum = maxOf(maxNum, nums[i])
if (nums[i] < maxNum) right = i
}
for (i in n - 1 downTo 0) {
minNum = minOf(minNum, nums[i])
if (nums[i] > minNum) left = i
}
if (right <= left) return intArrayOf(-1, -1)
return intArrayOf(left, right)
}
}
Python
class Solution:
def findUnsortedSubarrayBounds(self, nums: List[int]) -> Tuple[int, int]:
n = len(nums)
left, right = n - 1, 0
max_num, min_num = nums[0], nums[-1]
for i in range(n):
max_num = max(max_num, nums[i])
if nums[i] < max_num:
right = i
for i in range(n - 1, -1, -1):
min_num = min(min_num, nums[i])
if nums[i] > min_num:
left = i
if right <= left:
return (-1, -1)
return (left, right)
Rust
impl Solution {
pub fn find_unsorted_subarray_bounds(nums: Vec<i32>) -> Vec<i32> {
let n = nums.len();
let mut left = n - 1;
let mut right = 0;
let mut max_num = nums[0];
let mut min_num = nums[n - 1];
for i in 0..n {
max_num = max_num.max(nums[i]);
if nums[i] < max_num {
right = i;
}
}
for i in (0..n).rev() {
min_num = min_num.min(nums[i]);
if nums[i] > min_num {
left = i;
}
}
if right <= left {
return vec![-1, -1];
}
vec![left as i32, right as i32]
}
}
TypeScript
class Solution {
findUnsortedSubarrayBounds(nums: number[]): [number, number] {
const n = nums.length;
let left = n - 1, right = 0;
let maxNum = nums[0], minNum = nums[n - 1];
for (let i = 0; i < n; i++) {
maxNum = Math.max(maxNum, nums[i]);
if (nums[i] < maxNum) right = i;
}
for (let i = n - 1; i >= 0; i--) {
minNum = Math.min(minNum, nums[i]);
if (nums[i] > minNum) left = i;
}
if (right <= left) return [-1, -1];
return [left, right];
}
}
Complexity
- ⏰ Time complexity:
O(n)— Single pass from both ends, same as the main solution - 🧺 Space complexity:
O(1)— Only variables for boundaries and min/max, same as the main solution