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
JavaScript Program to Find the Longest Bitonic Subsequence | DP-15
We will be finding the longest bitonic subsequence in an array using dynamic programming. A bitonic subsequence is a sequence which is first increasing, then decreasing. To find the longest bitonic subsequence, we will use a two-step approach: first find the longest increasing subsequence ending at each position, then find the longest decreasing subsequence starting from each position.
What is a Bitonic Subsequence?
A bitonic subsequence has two parts:
Increasing part: Elements are in ascending order
Decreasing part: Elements are in descending order
For example, in [1, 7, 8, 11, 5, 2, 3], a bitonic subsequence could be [1, 7, 8, 11, 5, 2] (increases up to 11, then decreases).
Approach
The dynamic programming approach uses two arrays:
LIS array: Store the length of longest increasing subsequence ending at each index
LDS array: Store the length of longest decreasing subsequence starting from each index
For each position i, the longest bitonic subsequence through that position has length: LIS[i] + LDS[i] - 1
The -1 accounts for counting the element at position i twice
Example
Here is a complete JavaScript program to find the longest bitonic subsequence:
function LBSLength(arr) {
let n = arr.length;
let lis = new Array(n).fill(1); // Longest Increasing Subsequence
let lds = new Array(n).fill(1); // Longest Decreasing Subsequence
// Calculate LIS for each position
for (let i = 1; i < n; i++) {
for (let j = 0; j < i; j++) {
if (arr[i] > arr[j]) {
lis[i] = Math.max(lis[i], lis[j] + 1);
}
}
}
// Calculate LDS for each position (from right to left)
for (let i = n - 2; i >= 0; i--) {
for (let j = n - 1; j > i; j--) {
if (arr[i] > arr[j]) {
lds[i] = Math.max(lds[i], lds[j] + 1);
}
}
}
// Find maximum bitonic length
let maxLength = 0;
for (let i = 0; i < n; i++) {
maxLength = Math.max(maxLength, lis[i] + lds[i] - 1);
}
return maxLength;
}
// Test with example array
const arr = [1, 7, 8, 11, 5, 2, 3];
console.log("Array:", arr);
console.log("Length of Longest Bitonic Subsequence:", LBSLength(arr));
// Test with another example
const arr2 = [0, 8, 4, 12, 2, 10, 6, 14, 1, 9];
console.log("\nArray:", arr2);
console.log("Length of Longest Bitonic Subsequence:", LBSLength(arr2));
Array: [ 1, 7, 8, 11, 5, 2, 3 ] Length of Longest Bitonic Subsequence: 6 Array: [ 0, 8, 4, 12, 2, 10, 6, 14, 1, 9 ] Length of Longest Bitonic Subsequence: 7
How It Works
Let's trace through the algorithm with array [1, 7, 8, 11, 5, 2, 3]:
function traceAlgorithm(arr) {
let n = arr.length;
let lis = new Array(n).fill(1);
let lds = new Array(n).fill(1);
// Calculate LIS
for (let i = 1; i < n; i++) {
for (let j = 0; j < i; j++) {
if (arr[i] > arr[j]) {
lis[i] = Math.max(lis[i], lis[j] + 1);
}
}
}
console.log("LIS array:", lis);
// Calculate LDS
for (let i = n - 2; i >= 0; i--) {
for (let j = n - 1; j > i; j--) {
if (arr[i] > arr[j]) {
lds[i] = Math.max(lds[i], lds[j] + 1);
}
}
}
console.log("LDS array:", lds);
// Show bitonic lengths at each position
console.log("Bitonic lengths:");
for (let i = 0; i < n; i++) {
console.log(`Position ${i}: ${lis[i]} + ${lds[i]} - 1 = ${lis[i] + lds[i] - 1}`);
}
}
const arr = [1, 7, 8, 11, 5, 2, 3];
console.log("Array:", arr);
traceAlgorithm(arr);
Array: [ 1, 7, 8, 11, 5, 2, 3 ] LIS array: [ 1, 2, 3, 4, 1, 1, 2 ] LDS array: [ 4, 3, 2, 1, 3, 2, 1 ] Bitonic lengths: Position 0: 1 + 4 - 1 = 4 Position 1: 2 + 3 - 1 = 4 Position 2: 3 + 2 - 1 = 4 Position 3: 4 + 1 - 1 = 4 Position 4: 1 + 3 - 1 = 3 Position 5: 1 + 2 - 1 = 2 Position 6: 2 + 1 - 1 = 2
Time and Space Complexity
| Aspect | Complexity | Explanation |
|---|---|---|
| Time | O(n²) | Two nested loops for LIS and LDS calculation |
| Space | O(n) | Two arrays of size n for storing LIS and LDS values |
Conclusion
The longest bitonic subsequence problem is solved efficiently using dynamic programming by combining longest increasing and decreasing subsequences. This approach has O(n²) time complexity and finds the optimal solution for any input array.
