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.

Updated on: 2026-03-15T23:19:01+05:30

488 Views

Kickstart Your Career

Get certified by completing the course

Get Started
Advertisements