📜  使用段树的LIS

📅  最后修改于: 2021-04-17 02:14:30             🧑  作者: Mango

给您一个整数数组,您需要找到最长的递增子序列的长度。

可以有4种解决问题的方法。

1)蛮力:
在这种方法中,我们尝试找到所有增加的子序列,然后返回最长的增加的子序列的最大长度。为此,我们使用一个递归函数,该函数从当前元素开始返回LIS的长度。
时间复杂度: O(2^n)
空间复杂度: O(n^2)

2)动态编程
该方法依赖于以下事实:直到第i个索引的LIS都独立于后面的(n-i + 1)个元素。另外,可以通过检查从索引0到i获得的LIS来计算直到第(i + 1)个元素的LIS。
动态编程方法
时间复杂度: O(n^2)
空间复杂度: O(n)

3)使用二进制搜索
元素以升序存储在DP数组中,在其中使用二进制搜索确定索引。数组的长度给出了LIS的长度。
时间复杂度: O(nlog(n))
空间复杂度: O(n)
有关详细信息,请参阅最长递增子序列的构造(N log N)。

4)使用段树
首先将元素按升序排序,同时保留其原始索引。对于严格增加的LIS,对于相等的元素,具有较高索引的元素将比较低元素具有更早的位置。这可以存储在成对的数组中。
现在,将它们填充到细分树中。根据它们在已排序数组中的位置,将它们填充在对应于其原始索引的叶中的段树中。
最初,段树用零初始化。现在,让我们假设我们已经处理了排序数组中的第ith个元素。在第(i + 1)次迭代中,让值的原始位置为j。
然后,它将填充分段树中的第j个叶子,其值将是0到(j-1)+1之间的叶子的最大值。
(由在其前面的子数组中小于它的元素形成的LIS的长度,并为其包含+1)
Arr [] = {5,1,3,9}指数:{0,1,2,3}
Sorted_Arr [] = {1、3、5、9} Original_Indices:{1、2、0、3}指数:{0、1、2、3}

9(2)

9(4)

C++
// Finding the Longest Increasing Subsequence using
// Segment Tree
#include 
using namespace std;
 
// function to compare two pairs
int compare(pair p1, pair p2)
{
     /* For same values, element with the higher
        index appear earlier in the sorted array.
        This is for strictly increasing subsequence.
        For increasing subsequence, the lower index
         appears earlier in the sorted array. */
    if (p1.first == p2.first)
        return p1.second > p2.second;
     
    // Sorting the array according to their values.
    return p1.first < p2.first;
}
 
// Building the entire Segment tree, the root of which
// contains the length of the LIS
void buildTree(int* tree, int pos, int low, int high,
                                 int index, int value)
{
    // index is the original index of current element
    // If the index is not present in the given range,
    // then simply return
    if (index < low || index > high)
        return;
 
    // If low == high then the current position should
    // be updated to the value
    if (low == high) {
        tree[pos] = value;
        return;
    }
 
    int mid = (high + low) / 2;
 
    // Recursively call the function on the
    // child nodes
    buildTree(tree, 2 * pos + 1, low, mid, index, value);
    buildTree(tree, 2 * pos + 2, mid + 1, high, index, value);
 
    // Assign the current position the max of the 2 child
    // nodes
    tree[pos] = max(tree[2 * pos + 1], tree[2 * pos + 2]);
}
 
// Function to query the Segment tree and return the
// value for a given range
int findMax(int* tree, int pos, int low, int high,
                               int start, int end)
{
    // Query: Same as the query function of Segment tree
    // If the current range is totally inside the query
    // range, return the value of current position
    if (low >= start && high <= end)
        return tree[pos];
     
    // If it is out of bound, return the minimum which
    // would be 0 in this case
    if (start > high || end < low)
        return 0;   
 
    // Partial overlap
    int mid = (high + low) / 2;
 
    // Call findMax on child nodes recursively and
    // return the maximum of the two
    return max(findMax(tree, 2 * pos + 1, low, mid,
                                        start, end),
               findMax(tree, 2 * pos + 2, mid + 1,
                                high, start, end));
}
 
int findLIS(int arr[], int n)
{
    // The array of pairs stores the integers and
    // indices in p[i]
    pair p[n];
    for (int i = 0; i < n; i++) {
        p[i].first = arr[i];
        p[i].second = i;
    }
 
    // Sorting the array in increasing order
    // of the elements
    sort(p, p + n, compare);
 
    // Calculating the length of the segment-tree
    int len = pow(2, (int)(ceil(sqrt(n))) + 1) - 1;
    int tree[len];
 
    // Initializing the tree with zeroes
    memset(tree, 0, sizeof(tree));
 
    // Building the segment-tree, the root node of
    // which contains the length of LIS for the n
    // elements
    for (int i = 0; i < n; i++) {
        buildTree(tree, 0, 0, n - 1, p[i].second,
     findMax(tree, 0, 0, n - 1, 0, p[i].second) + 1);
    }
     
    return tree[0];
}
 
// Driver code
int main()
{
    int arr[] = { 10, 22, 9, 33, 21, 50, 41, 60 };
    int n = sizeof(arr) / sizeof(arr[0]);
    cout << "Length of the LIS: " << findLIS(arr, n);
    return 0;
}


Java
// Finding the Longest Increasing Subsequence
// using Segment Tree
import java.io.*;
import java.util.*;
 
class Pair
{
    int first;
    int second;
}
 
class GFG{
 
// Building the entire Segment tree, the root of which
// contains the length of the LIS
static void buildTree(int[] tree, int pos, int low,
                      int high, int index, int value)
{
     
    // Index is the original index of current element
    // If the index is not present in the given range,
    // then simply return
    if (index < low || index > high)
        return;
 
    // If low == high then the current position
    // should be updated to the value
    if (low == high)
    {
        tree[pos] = value;
        return;
    }
 
    int mid = (high + low) / 2;
 
    // Recursively call the function on the
    // child nodes
    buildTree(tree, 2 * pos + 1, low, mid,
              index, value);
    buildTree(tree, 2 * pos + 2, mid + 1, high,
              index, value);
 
    // Assign the current position the max of
    // the 2 child nodes
    tree[pos] = Math.max(tree[2 * pos + 1],
                         tree[2 * pos + 2]);
}
 
// Function to query the Segment tree and
// return the value for a given range
static int findMax(int[] tree, int pos, int low,
                   int high, int start, int end)
{
     
    // Query: Same as the query function of Segment
    // tree. If the current range is totally inside
    // the query range, return the value of current
    // position
    if (low >= start && high <= end)
        return tree[pos];
 
    // If it is out of bound, return the minimum
    // which would be 0 in this case
    if (start > high || end < low)
        return 0;
 
    // Partial overlap
    int mid = (high + low) / 2;
 
    // Call findMax on child nodes recursively
    // and return the maximum of the two
    return Math.max(findMax(tree, 2 * pos + 1, low, mid,
                            start, end),
                    findMax(tree, 2 * pos + 2, mid + 1,
                            high, start, end));
}
 
static int findLIS(int arr[], int n)
{
     
    // The array of pairs stores the integers
    // and indices in p[i]
    List p = new ArrayList();
 
    for(int i = 0; i < n; i++)
    {
        Pair p1 = new Pair();
        p1.first = arr[i];
        p1.second = i;
        p.add(p1);
    }
 
    // Sorting the array in increasing order
    // of the elements
    Collections.sort(p, (p1, p2) ->
    {
         
        /* For same values, element with the higher
           index appear earlier in the sorted array.
           This is for strictly increasing subsequence.
           For increasing subsequence, the lower index
            appears earlier in the sorted array. */
        if (p1.first == p2.first)
            return p2.second - p1.second;
 
        // Sorting the array according to their values.
        return p1.first - p2.first;
    });
 
    // Calculating the length of the segment-tree
    int len = (int)(Math.pow(
              2, (int)(Math.ceil(Math.sqrt(n))) + 1)) - 1;
    int[] tree = new int[len];
 
    // Building the segment-tree, the root node of
    // which contains the length of LIS for the n
    // elements
    for(int i = 0; i < n; i++)
    {
        buildTree(tree, 0, 0, n - 1, p.get(i).second,
                  findMax(tree, 0, 0, n - 1, 0,
                          p.get(i).second) + 1);
    }
    return tree[0];
}
 
// Driver Code
public static void main(String[] args)
{
    int arr[] = { 10, 22, 9, 33, 21, 50, 41, 60 };
    int n = arr.length;
     
    System.out.println("Length of the LIS: " +
                       findLIS(arr, n));
}
}
 
// This code is contributed by jithin


Length of the LIS: 5

时间复杂度: O(nlogn)

空间复杂度: O(nlogn)