📜  O(n log n) 中最长的双调子序列

📅  最后修改于: 2022-05-13 01:57:49.256000             🧑  作者: Mango

O(n log n) 中最长的双调子序列

给定一个包含 n 个正整数的数组 arr[0 … n-1],如果 arr[] 的子序列先递增,然后递减,则称为双调。编写一个将数组作为参数并返回最长双音子序列长度的函数。
一个按升序排序的序列被认为是双调的,降序部分为空。类似地,降序序列被认为是双调,增加的部分为空。
预期时间复杂度:O(n log n)

例子:

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)

我们在动态规划 | 中讨论了 O(n 2 ) 解。 Set 15(最长双调子序列)
这个想法是按照最长递增子序列大小 (n log n) 来查看最长递增子序列 (LIS) 长度的计算方式。

算法:

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++
// C++ program to find length of longest
// Bitonic subsequence in O (n Log n( time,
#include 
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;
}


Java
// 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


Python3
# Python3 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
        else if (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
        else if (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


C#
// 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.


PHP
 $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
?>


Javascript


输出:

7

时间复杂度: O(nLogn)
辅助空间: O(n)