Article Categories
- All Categories
-
Data Structure
-
Networking
-
RDBMS
-
Operating System
-
Java
-
MS Excel
-
iOS
-
HTML
-
CSS
-
Android
-
Python
-
C Programming
-
C++
-
C#
-
MongoDB
-
MySQL
-
Javascript
-
PHP
-
Economics & Finance
Can split array into consecutive subsequences in JavaScript
We are required to write a JavaScript function that takes in an array of sorted integers and determines if we can split it into consecutive subsequences of at least 3 elements each.
Problem Statement
Our function should return true if we can split the array into 1 or more subsequences where each subsequence consists of consecutive integers and has length at least 3, false otherwise.
Input:
const arr = [1, 2, 3, 3, 4, 5];
Expected Output:
true
Explanation: We can split the array into two consecutive subsequences:
1, 2, 3 3, 4, 5
Algorithm Approach
The solution uses a greedy approach with two key data structures:
- count: Tracks frequency of each number
- needed: Tracks how many numbers are needed to extend existing subsequences
For each number, we either extend an existing subsequence or start a new one with at least 3 consecutive elements.
Implementation
const arr = [1, 2, 3, 3, 4, 5];
const canSplit = (arr = []) => {
// Count frequency of each number
const count = arr.reduce((acc, num) => {
acc[num] = (acc[num] || 0) + 1;
return acc;
}, {});
// Track numbers needed to extend subsequences
const needed = {};
for (const num of arr) {
// Skip if this number is already used
if (count[num] <= 0) {
continue;
}
count[num] -= 1;
// Try to extend existing subsequence
if (needed[num] > 0) {
needed[num] -= 1;
needed[num + 1] = (needed[num + 1] || 0) + 1;
}
// Try to start new subsequence of length 3
else if (count[num + 1] > 0 && count[num + 2] > 0) {
count[num + 1] -= 1;
count[num + 2] -= 1;
needed[num + 3] = (needed[num + 3] || 0) + 1;
}
// Cannot form valid subsequence
else {
return false;
}
}
return true;
};
console.log(canSplit(arr));
true
Step-by-Step Execution
Let's trace through the algorithm with [1, 2, 3, 3, 4, 5]:
const arr = [1, 2, 3, 3, 4, 5];
console.log("Initial count:", {1: 1, 2: 1, 3: 2, 4: 1, 5: 1});
// Processing num = 1: Start new subsequence [1,2,3]
// Processing num = 2: Already used in subsequence [1,2,3]
// Processing num = 3: Extend subsequence to [1,2,3,4]
// Processing num = 3: Start new subsequence [3,4,5]
// Processing num = 4: Already used in subsequences
// Processing num = 5: Already used in subsequence [3,4,5]
console.log("Result: All numbers successfully placed in valid subsequences");
Initial count: { '1': 1, '2': 1, '3': 2, '4': 1, '5': 1 }
Result: All numbers successfully placed in valid subsequences
Additional Test Cases
// Test case 1: Valid split
console.log("Test 1:", canSplit([1, 2, 3, 3, 4, 4, 5, 5])); // true
// Test case 2: Invalid - cannot form subsequence of length 3
console.log("Test 2:", canSplit([1, 2, 3, 4, 4, 5])); // false
// Test case 3: Valid - single long subsequence
console.log("Test 3:", canSplit([1, 2, 3, 4, 5])); // true
Test 1: true Test 2: false Test 3: true
Time and Space Complexity
- Time Complexity: O(n) where n is the length of the array
- Space Complexity: O(k) where k is the number of unique elements
Conclusion
This greedy algorithm efficiently determines if an array can be split into consecutive subsequences of length ? 3. It prioritizes extending existing subsequences over creating new ones, ensuring optimal partitioning.
