Longest Bitonic Subsequence in O(n log n)

Given an array arr[0 … n-1] containing n positive integers, a subsequence of arr[] is called Bitonic if it is first increasing, then decreasing. Write a function that takes an array as argument and returns the length of the longest bitonic subsequence.
A sequence, sorted in increasing order is considered Bitonic with the decreasing part as empty. Similarly, decreasing order sequence is considered Bitonic with the increasing part as empty.

Expected Time Complexity: O(n Log n)

Examples:

Input arr[] = {1, 11, 2, 10, 4, 5, 2, 1};
Output: 6 
Explanation : A Longest Bitonic Subsequence 
            of length 6 is 1, 2, 10, 4, 2, 1

Input arr[] = {12, 11, 40, 5, 3, 1}
Output: 5 
Explanation : A Longest Bitonic Subsequence 
            of length 5 is 12, 11, 5, 3, 1)

Input arr[] = {80, 60, 30, 40, 20, 10}
Output: 5 
Explanation : A Longest Bitonic Subsequence
         of length 5 is 80, 60, 30, 20, 10)

We have discussed O(n2) solution in Dynamic Programming | Set 15 (Longest Bitonic Subsequence)

The idea is to follow Longest Increasing Subsequence Size (n log n) to see the way length of Longest Increasing subsequence (LIS) is calculated.

Algorithm:

Step 1: Define 4 Auxiliary arrays of size n:
        increasing[n] to calculate LIS of the 
        array  tail1[n] to store the values for 
        LIS for increasing[n]
        decreasing[n] to calculate LIS of the 
        array  tail2[n] to store the values for
        LIS for decreasing[n]
Step 2: Find LIS for increasing array
Step 3: Reverse array and store it in decreasing
Step 4: Find LIS for decreasing array
Step 5: Longest Bitonicc SubSequence length now 
        will be max of tail1[i] + tail2[i] + 1

C++

filter_none

edit
close

play_arrow

link
brightness_4
code

// C++ program to find length of longest
// Bitonic subsequence in O (n Log n( tine,
#include <bits/stdc++.h>
using namespace std;
  
// Binary Search
int ceilIndex(int arr[], int l, int r, int x)
{
    if (l > r)
        return -1;
  
    int mid = l + (r - l) / 2;
    if (arr[mid] == x)
        return mid;
  
    if (x < arr[mid])
        return ceilIndex(arr, l, mid - 1, x);
  
    return ceilIndex(arr, mid + 1, r, x);
  
}
  
// function to reverse an array
void revereseArr(int arr[], int n)
{
    int i = 0;
    int j = n - 1;
    while (i < j)
       swap(arr[i++], arr[j--]);
}
  
// Returns length of longest Bitonic
// subsequence in O(n Log n) time.
int getLBSLengthLogn(int arr[], int n)
{
    // Base Case:
    if (n == 0)
        return 0;
  
  
    // Aux array storing the input array
    // in same order to calculate LIS:
    int increasing[n];
    int tail1[n];  // To store lengths of IS
  
    // Aux array storing the input array
    // in reverse order to calculate LIS:
    // This will calculate Longest Decreasing
    // Subsequence which is required for
    // Longest Bitonic Subsequence
    int decreasing[n];
    int tail2[n]; // To store lengths of DS
  
  
    // initializing first index same as
    // original array:
    increasing[0] = arr[0];
  
    // index in initialized as 1 from where
    // the remaining computations will be done
    int in = 1;
  
    // tail1 stores Longest Increasing subsequence
    // length values till index in
    tail1[0] = 0;
  
    // remaining computations to get the
    // LIS length for increasing
    for (int i = 1; i < n; i++)
    {
        if (arr[i] < increasing[0])
        {
            increasing[0] = arr[i];
            tail1[i] = 0;
        }
        else if (arr[i] > increasing[in - 1])
        {
            increasing[in++] = arr[i];
            tail1[i] = in - 1;
        }
        else
        {
            increasing[ceilIndex(increasing, -1,
                        in - 1, arr[i])] = arr[i];
            tail1[i] = ceilIndex(increasing, -1,
                                   in - 1, arr[i]);
        }
    }
  
    // reiitializing the index to 1
    in = 1;
  
    // reversing the array to get the Longest
    // Decreasing Subsequence Length:
    revereseArr(arr, n);
    decreasing[0] = arr[0];
    tail2[0] = 0;
  
    for (int i = 1; i < n; i++)
    {
        if (arr[i] < decreasing[0])
        {
            decreasing[0] = arr[i];
            tail2[i] = 0;
        }
        else if (arr[i] > decreasing[in - 1])
        {
            decreasing[in++] = arr[i];
            tail2[i] = in - 1;
        }
        else
        {
            decreasing[ceilIndex(decreasing, -1,
                      in - 1, arr[i])] = arr[i];
            tail2[i] = ceilIndex(decreasing, -1,
                                 in - 1, arr[i]);
        }
    }
  
    revereseArr(arr, n);
    revereseArr(tail2, n);
  
    // Longest Bitonic Subsequence length is
    // maximum of tail1[i] + tail2[i] + 1:
    int ans = 0;
    for (int i = 0; i < n; i++)
        if (ans < (tail1[i] + tail2[i] + 1))
            ans = (tail1[i] + tail2[i] + 1);
  
  
    return ans;
}
  
// Driver code
int main()
{
    int arr[] = { 0, 8, 4, 12, 2, 10, 6, 14,
                  1, 9, 5, 13, 3, 11, 7, 15
                };
    int n = sizeof(arr) / sizeof(arr[0]);
    cout << getLBSLengthLogn(arr, n) << endl;
    return 0;
}

chevron_right


Java

filter_none

edit
close

play_arrow

link
brightness_4
code

// Java program to find length of longest
// Bitonic subsequence in O (n Log n) time,
import java.util.Arrays;
public class GFG 
{
    // function to reverse an array
    static void revereseArr(int arr[])
    {
        int i = 0;
        int j = arr.length - 1;
        while (i < j)
        {
            int temp = arr[i];
            arr[i] = arr[j];
            arr[j] = temp;
            i++;j--;
        }
    }
      
    // Returns length of longest Bitonic
    // subsequence in O(n Log n) time.
    static int getLBSLengthLogn(int arr[])
    {
        int n = arr.length;
          
        // Base Case:
        if(n==0)
            return 0;
          
        /*Aux array storing the input array
        in same order to calculate LIS:*/
        int increasing[] = new int[n];
          
        //to store lengths of IS
        int tail1[] = new int[n]; 
          
        /*Aux array storing the input array
          in reverse order to calculate LIS:
          This will calculate Longest Decreasing
          Subsequence which is required for
          Longest Bitonic Subsequence*/
        int decreasing[] = new int[n];
          
        // To store lengths of DS
        int tail2[] = new int[n];
          
        // initializing first index same as
        // original array:
        increasing[0] = arr[0];
          
        // index in initialized as 1 from where
        // the remaining computations will be done
        int in = 1;
          
        // tail1 stores Longest Increasing 
        // subsequence length values till index in
        tail1[0] = 0;
          
        // remaining computations to get the
        // LIS length for increasing
        for(int i = 1; i < n; i++)
        {
            if(arr[i] < increasing[0])
            {
                increasing[0] = arr[i];
                tail1[i] = 0;
            }
            else if(arr[i] > increasing[in - 1])
            {
                increasing[in++] = arr[i];
                tail1[i] = in - 1;
            }
            else
            {
                int getIndex1 = Arrays.binarySearch(increasing,
                                                0, in, arr[i]);
                if(getIndex1 <= -1)
                    continue;
                  
                increasing[getIndex1] = arr[i];
                tail1[i] = getIndex1;
            }
        }
  
        // reinitializing the index to 1
        in = 1;
          
        // reversing the array to get the Longest
        // Decreasing Subsequence Length:
        revereseArr(arr);
        decreasing[0] = arr[0];
        tail2[0] = 0;
          
        for(int i = 0; i < n; i++)
        {
            if(arr[i] < decreasing[0])
            {
                decreasing[0] = arr[i];
                tail2[i] = 0;
            }
            else if(arr[i] > decreasing[in - 1])
            {
                decreasing[in++] = arr[i];
                tail2[i] = in - 1;
            }
            else
            {
                int getIndex2 =  Arrays.binarySearch(decreasing,
                                                0, in , arr[i]);
                if(getIndex2 <= -1)
                    continue;
              
                decreasing[getIndex2] = arr[i];
                tail2[i] = getIndex2;
              
            }
        }
          
        revereseArr(arr);
        revereseArr(tail2);
       
        // Longest Bitonic Subsequence length is
        // maximum of tail1[i] + tail2[i] + 1:
        int ans = 0;
        for (int i = 0; i < n; i++)
        {
            if (ans < (tail1[i] + tail2[i] + 1))
                ans = (tail1[i] + tail2[i] + 1);
        }
        return ans;
    }
  
    // Driver code to test above methods
    public static void main(String[] args) 
    {
        int arr[] = { 0, 8, 4, 12, 2, 10, 6, 14,
                      1, 9, 5, 13, 3, 11, 7, 15 };
        System.out.println(getLBSLengthLogn(arr));
    }
}
// This code is contributed by Sumit Ghosh

chevron_right


Python

filter_none

edit
close

play_arrow

link
brightness_4
code

# C++ program to find length of longest
# Bitonic subsequence in O (n Log n) time
   
# Binary Search
def ceilIndex(arr, l, r, x):
    if (l > r):
        return -1
      
    mid = l + (r - l) / 2
    if (arr[mid] == x):
        return mid
      
    if (x < arr[mid]):
        return ceilIndex(arr, l, mid - 1, x)
      
    return ceilIndex(arr, mid + 1, r, x)
      
# Function to reverse an array
def revereseArr(arr, n):
    i = 0
    j = n - 1
    while (i < j):
        t = arr[i]
        arr[i] = arr[j]
        arr[j] = t
        i += 1
        j -= 1
         
# Returns length of longest Bitonic
# subsequence in O(n Log n) time.
def getLBSLengthLogn(arr, n):
      
    # Base Case:
    if (n == 0):
        return 0
      
    # Aux array storing the input array
    # in same order to calculate LIS:
    increasing = [0 for i in range(n)]
    tail1 = [0 for i in range(n)]  # To store lengths of LIS
   
    # Aux array storing the input array
    # in reverse order to calculate LIS:
    # This will calculate Longest Decreasing
    # Subsequence which is required for
    # Longest Bitonic Subsequence
    decreasing = [0 for i in range(n)]
    tail2 = [0 for i in range(n)]   # To store lengths of DS
      
    # initializing first index same as
    # original array:
    increasing [0] = arr [0]
   
    # index in initialized as 1 from where
    # the remaining computations will be done
    ind = 1
      
    # tail1 stores Longest Increasing subsequence
    # length values till index in
    tail1[0] = 0
      
    # remaining computations to get the
    # LIS length for increasing
    for i in range(1, n):
        if (arr[i] < increasing[0]):
            increasing[0] = arr[i]
            tail1[i] = 0
        elif (arr[i] > increasing[ind - 1]):
            increasing[ind] = arr[i]
            ind += 1
            tail1[i] = ind - 1
        else:
            increasing[ceilIndex(increasing, -1,
                        ind - 1, arr[i])] = arr[i]
            tail1[i] = ceilIndex(increasing, -1,
                                   ind- 1, arr[i])
   
    # reinitializing the index to 1
    ind = 1
  
    # reversing the array to get the Longest
    # Decreasing Subsequence Length:
    revereseArr(arr, n)
    decreasing[0] = arr[0]
    tail2[0] = 0
    for i in range(1, n):
        if (arr[i] < decreasing[0]):
            decreasing[0] = arr[i]
            tail2[i] = 0
        elif (arr[i] > decreasing[ind - 1]):
            decreasing[ind] = arr[i]
            ind += 1
            tail2[i] = ind - 1
        else:
            decreasing[ceilIndex(decreasing, -1,
                      ind - 1, arr[i])] = arr[i]
            tail2[i] = ceilIndex(decreasing, -1,
                                 ind - 1, arr[i])
    revereseArr(arr, n)
    revereseArr(tail2, n)
    # Longest Bitonic Subsequence length is
    # maximum of tail1[i] + tail2[i] + 1:
    ans = 0
    for i in range(n):
        if (ans < (tail1[i] + tail2[i] + 1)):
            ans = (tail1[i] + tail2[i] + 1)
      
    return ans
      
# Driver code
arr = [ 0, 8, 4, 12, 2, 10, 6, 14,
                  1, 9, 5, 13, 3, 11, 7, 15]
n = len(arr)
print getLBSLengthLogn(arr, n)
  
# This code is contributed by Sachin Bisht

chevron_right


C#

filter_none

edit
close

play_arrow

link
brightness_4
code

// C# program to find length of longest
// Bitonic subsequence in O (n Log n) time,
using System;
  
public class GFG {
      
    // function to reverse an array
    static void revereseArr(int []arr)
    {
        int i = 0;
        int j = arr.Length - 1;
        while (i < j)
        {
            int temp = arr[i];
            arr[i] = arr[j];
            arr[j] = temp;
            i++;
            j--;
        }
    }
      
    // Returns length of longest Bitonic
    // subsequence in O(n Log n) time.
    static int getLBSLengthLogn(int []arr)
    {
        int n = arr.Length;
          
        // Base Case:
        if(n == 0)
            return 0;
          
        /*Aux array storing the input array
        in same order to calculate LIS:*/
        int []increasing = new int[n];
          
        //to store lengths of IS
        int []tail1 = new int[n]; 
          
        /*Aux array storing the input array
        in reverse order to calculate LIS:
        This will calculate Longest Decreasing
        Subsequence which is required for
        Longest Bitonic Subsequence*/
        int []decreasing = new int[n];
          
        // To store lengths of DS
        int []tail2 = new int[n];
          
        // initializing first index same as
        // original array:
        increasing[0] = arr[0];
          
        // index in initialized as 1 from where
        // the remaining computations will be done
        int inn = 1;
          
        // tail1 stores Longest Increasing 
        // subsequence length values till index in
        tail1[0] = 0;
          
        // remaining computations to get the
        // LIS length for increasing
        for(int i = 1; i < n; i++)
        {
            if(arr[i] < increasing[0])
            {
                increasing[0] = arr[i];
                tail1[i] = 0;
            }
            else if(arr[i] > increasing[inn - 1])
            {
                increasing[inn++] = arr[i];
                tail1[i] = inn - 1;
            }
            else
            {
                int getIndex1 = Array.BinarySearch(increasing,
                                                0, inn, arr[i]);
                if(getIndex1 <= -1)
                    continue;
                  
                increasing[getIndex1] = arr[i];
                tail1[i] = getIndex1;
            }
        }
  
        // reinitializing the index to 1
        inn = 1;
          
        // reversing the array to get the Longest
        // Decreasing Subsequence Length:
        revereseArr(arr);
        decreasing[0] = arr[0];
        tail2[0] = 0;
          
        for(int i = 0; i < n; i++)
        {
            if(arr[i] < decreasing[0])
            {
                decreasing[0] = arr[i];
                tail2[i] = 0;
            }
            else if(arr[i] > decreasing[inn - 1])
            {
                decreasing[inn++] = arr[i];
                tail2[i] = inn - 1;
            }
            else
            {
                int getIndex2 = Array.BinarySearch(decreasing,
                                             0, inn , arr[i]);
                if(getIndex2 <= -1)
                    continue;
              
                decreasing[getIndex2] = arr[i];
                tail2[i] = getIndex2;
              
            }
        }
          
        revereseArr(arr);
        revereseArr(tail2);
      
        // Longest Bitonic Subsequence length is
        // maximum of tail1[i] + tail2[i] + 1:
        int ans = 0;
        for (int i = 0; i < n; i++)
        {
            if (ans < (tail1[i] + tail2[i] + 1))
                ans = (tail1[i] + tail2[i] + 1);
        }
        return ans;
    }
  
    // Driver code to test above methods
    public static void Main() 
    {
        int []arr = { 0, 8, 4, 12, 2, 10, 6, 14,
                     1, 9, 5, 13, 3, 11, 7, 15 };
        Console.Write(getLBSLengthLogn(arr));
    }
}
  
// This code is contributed by nitin mittal.

chevron_right


PHP

filter_none

edit
close

play_arrow

link
brightness_4
code

<?php 
// PHP program to find length of longest
// Bitonic subsequence in O (n Log n( tine,
  
// Binary Search
function ceilIndex(&$arr, $l, $r, $x)
{
    if ($l > $r)
        return -1;
  
    $mid = intval($l + ($r - $l) / 2);
    if ($arr[$mid] == $x)
        return $mid;
  
    if ($x < $arr[$mid])
        return ceilIndex($arr, $l
                         $mid - 1, $x);
  
    return ceilIndex($arr, $mid + 1, $r, $x);
}
  
// function to reverse an array
function revereseArr(&$arr, $n)
{
    $i = 0;
    $j = $n - 1;
    while ($i < $j)
    {
        $temp = $arr[$i];
        $arr[$i] = $arr[$j];
        $arr[$j] = $temp;
        $i++;
        $j--;
    }
}
  
// Returns length of longest Bitonic
// subsequence in O(n Log n) time.
function getLBSLengthLogn(&$arr, $n)
{
    // Base Case:
    if ($n == 0)
        return 0;
  
    // Aux array storing the input array
    // in same order to calculate LIS:
    $increasing = array_fill(0, $n, NULL);
    $tail1 = array_fill(0, $n, NULL); // To store lengths of IS
  
    // Aux array storing the input array
    // in reverse order to calculate LIS:
    // This will calculate Longest Decreasing
    // Subsequence which is required for
    // Longest Bitonic Subsequence
    $decreasing = array_fill(0, $n, NULL);
    $tail2 = array_fill(0, $n, NULL); // To store lengths of DS
  
    // initializing first index same as
    // original array:
    $increasing[0] = $arr[0];
  
    // index in initialized as 1 from where
    // the remaining computations will be done
    $in = 1;
  
    // tail1 stores Longest Increasing 
    // subsequence length values till index in
    $tail1[0] = 0;
  
    // remaining computations to get the
    // LIS length for increasing
    for ($i = 1; $i < $n; $i++)
    {
        if ($arr[$i] < $increasing[0])
        {
            $increasing[0] = $arr[$i];
            $tail1[$i] = 0;
        }
        else if ($arr[$i] > $increasing[$in - 1])
        {
            $increasing[$in++] = $arr[$i];
            $tail1[$i] = $in - 1;
        }
        else
        {
            $increasing[ceilIndex($increasing, -1,
                        $in - 1, $arr[$i])] = $arr[$i];
            $tail1[$i] = ceilIndex($increasing, -1,
                                   $in - 1, $arr[$i]);
        }
    }
  
    // reiitializing the index to 1
    $in = 1;
  
    // reversing the array to get the Longest
    // Decreasing Subsequence Length:
    revereseArr($arr, $n);
    $decreasing[0] = $arr[0];
    $tail2[0] = 0;
  
    for ($i = 1; $i < $n; $i++)
    {
        if ($arr[$i] < $decreasing[0])
        {
            $decreasing[0] = $arr[$i];
            $tail2[$i] = 0;
        }
        else if ($arr[$i] > $decreasing[$in - 1])
        {
            $decreasing[$in++] = $arr[$i];
            $tail2[$i] = $in - 1;
        }
        else
        {
            $decreasing[ceilIndex($decreasing, -1,
                    $in - 1, $arr[$i])] = $arr[$i];
            $tail2[$i] = ceilIndex($decreasing, -1,
                                $in - 1, $arr[$i]);
        }
    }
  
    revereseArr($arr, $n);
    revereseArr($tail2, $n);
  
    // Longest Bitonic Subsequence length is
    // maximum of tail1[i] + tail2[i] + 1:
    $ans = 0;
    for ($i = 0; $i < $n; $i++)
        if ($ans < ($tail1[$i] + $tail2[$i] + 1))
            $ans = ($tail1[$i] + $tail2[$i] + 1);
  
    return $ans;
}
  
// Driver code
$arr = array(0, 8, 4, 12, 2, 10, 6, 14,
             1, 9, 5, 13, 3, 11, 7, 15);
$n = sizeof($arr);
echo getLBSLengthLogn($arr, $n) . "\n";
  
// This code is contributed by ita_c
?>

chevron_right



Output:

 7

Time Complexity: O(nLogn)
Auxiliary Space: O(n)

This article is contributed by Ved Vasavada. If you like GeeksforGeeks and would like to contribute, you can also write an article using contribute.geeksforgeeks.org or mail your article to contribute@geeksforgeeks.org. See your article appearing on the GeeksforGeeks main page and help other Geeks.

Please write comments if you find anything incorrect, or you want to share more information about the topic discussed above.



My Personal Notes arrow_drop_up

Improved By : nitin mittal, Ita_c