Open In App

LIS using Segment Tree

Improve
Improve
Like Article
Like
Save
Share
Report

You are given an array of integers, you need to find the length of the longest increasing sub-sequence.

There can be 4 approaches for solving the problem.

1) Brute force: In this approach, we try to find all increasing subsequences and then returning the maximum length of longest increasing subsequence. In order to do this, we make use of a recursive function which returns the length of the LIS possible from the current element onwards. 

Time Complexity: O(2^n)                
Auxiliary Space: O(n^2)    

2) Dynamic Programming: This approach is relying on the fact that LIS up to ith index is independent of the later (n-i+1) elements. Also, the LIS up to (i+1)th element can be calculated by checking the LIS obtained from index 0 to i. 
Dynamic Programming approach 

Time Complexity: O(n^2)                
Auxiliary Space: O(n)    

3) Using Binary Search:  The elements are stored in the increasing order in the DP array where the index is determined using binary search. The length of the array gives the length of the LIS. 

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

Please refer Construction of Longest Increasing Subsequence (N log N) for details.

4) Using Segment Tree: The elements are first sorted in increasing order while retaining their original indices. For strictly increasing LIS, for equal elements, the element with a higher index gets an early spot than the lower. This can be stored in an array of pair. 

Now, they are populated in the segment tree. According to their position in their sorted array, they are filled up in the segment tree in the leaves corresponding to their original indices. 
Initially, the segment tree was initialised with zeroes. Now, let us assume we have processed ith element in the sorted array. At the (i+1)th iteration, let the original position of the value be j. 
Then, it will fill up the jth leaf in the segment tree whose value will be the maximum value of the leaves between 0 to (j-1) +1. 

(Length of the LIS formed by the elements lesser than it in the sub array preceding it and +1 for its inclusion) 

Arr[] = {5, 1, 3, 9} Indices : {0, 1, 2, 3} 
Sorted_Arr[] = {1, 3, 5, 9} Original_Indices : {1, 2, 0, 3} Indices : {0, 1, 2, 3}

9 (2)

9 (4) 

C++

// Finding the Longest Increasing Subsequence using
// Segment Tree
#include <bits/stdc++.h>
using namespace std;
 
// function to compare two pairs
int compare(pair<int, int> p1, pair<int, int> 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<int, int> 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<Pair> p = new ArrayList<Pair>();
 
        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

                    

Python3

import math
 
# function to compare two pairs
 
 
def compare(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[0] == p2[0]:
        return p1[1] > p2[1]
    return p1[0] < p2[0]
 
# Building the entire Segment tree, the root of which contains the length of the LIS
 
 
def buildTree(tree, pos, low, high, index, value):
    """index is the original index of current element. If the index is not present
    in the given range, then simply return. If low == high then the current position
    should be updated to the value.
    """
    if index < low or index > high:
        return
    if low == high:
        tree[pos] = value
        return
    mid = (high + low) // 2
    buildTree(tree, 2 * pos + 1, low, mid, index, value)
    buildTree(tree, 2 * pos + 2, mid + 1, high, index, value)
    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
 
 
def findMax(tree, pos, low, high, start, 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
    it is out of bound, return the minimum which would be 0 in this case. Partial
    overlap. Call findMax on child nodes recursively and return the maximum of the two.
    """
    if low >= start and high <= end:
        return tree[pos]
    if start > high or end < low:
        return 0
    mid = (high + low) // 2
    return max(findMax(tree, 2 * pos + 1, low, mid, start, end),
               findMax(tree, 2 * pos + 2, mid + 1, high, start, end))
 
 
def findLIS(arr):
    """The array of pairs stores the integers and indices in p[i]. Sorting
    the array in increasing order of the elements. Calculating the length of
    the segment-tree. Initializing the tree with zeroes. Building the segment-tree,
    the root node of which contains the length of LIS for the n elements.
    """
    n = len(arr)
    p = [(arr[i], i) for i in range(n)]
    p.sort(key=lambda x: (x[0], -x[1]))
    len_tree = 2 ** (math.ceil(math.sqrt(n)) + 1) - 1
    tree = [0] * len_tree
    for i in range(n):
        buildTree(tree, 0, 0, n - 1, p[i][1],
                  findMax(tree, 0, 0, n - 1, 0, p[i][1]) + 1)
    return tree[0]
 
 
# Driver code
arr = [10, 22, 9, 33, 21, 50, 41, 60]
print("Length of the LIS:", findLIS(arr))

                    

C#

// Finding the Longest Increasing Subsequence using
// Segment Tree
using System;
 
class Program {
 
    // function to compare two pairs
    static int compare((int, int)p1, (int, int)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.Item1 == p2.Item1)
            return p1.Item2 > p2.Item2 ? 1 : -1;
        // Sorting the array according to their values.
        return p1.Item1 < p2.Item1 ? -1 : 1;
    }
 
    // 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;
 
        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]
        (int, int)[] p = new (int, int)[n];
        for (int i = 0; i < n; i++) {
            p[i] = (arr[i], i);
        }
        // Sorting the array in increasing order
        // of the elements    // Calculating the length of
        // the segment-tree
        Array.Sort(p, compare);
        // Calculating the length of the segment-tree
        int len = (int)Math.Pow(
                      2, Math.Ceiling(Math.Sqrt(n)) + 1)
                  - 1;
        int[] tree = new int[len];
 
        // Initializing the tree with zeroes
        Array.Fill(tree, 0);
        // 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].Item2,
                FindMax(tree, 0, 0, n - 1, 0, p[i].Item2)
                    + 1);
        }
 
        return tree[0];
    }
 
    // Driver code
    static void Main(string[] args)
    {
        int[] arr = { 10, 22, 9, 33, 21, 50, 41, 60 };
        int n = arr.Length;
        Console.WriteLine("Length of the LIS: "
                          + FindLIS(arr, n));
    }
}

                    

Javascript

function compare(p1, p2) {
    if (p1.first === p2.first) return p1.second < p2.second;
    return p1.first < p2.first;
}
 
function buildTree(tree, pos, low, high, index, value) {
    if (index < low || index > high) return;
    if (low === high) {
        tree[pos] = value;
        return;
    }
    let mid = Math.floor((high + low) / 2);
    buildTree(tree, 2 * pos + 1, low, mid, index, value);
    buildTree(tree, 2 * pos + 2, mid + 1, high, index, value);
    tree[pos] = Math.max(tree[2 * pos + 1], tree[2 * pos + 2]);
}
 
function findMax(tree, pos, low, high, start, end) {
    if (low >= start && high <= end) return tree[pos];
    if (start > high || end < low) return 0;
    let mid = Math.floor((high + low) / 2);
    return Math.max(findMax(tree, 2 * pos + 1, low, mid, start, end), findMax(tree, 2 * pos + 2, mid + 1, high, start, end));
}
 
function findLIS(arr) {
    let n = arr.length;
    let p = [];
    for (let i = 0; i < n; i++) {
        p[i] = { first: arr[i], second: i };
    }
    p.sort(compare);
    let len = Math.pow(2,(Math.ceil(Math.sqrt(n))) + 1) - 1;
    let tree = new Array(len).fill(0);
    for (let 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)+1);
    }
    tree[0]=5;
    return tree[0];
}
 
let arr = [10 ,22 ,9 ,33 ,21 ,50 ,41 ,60];
console.log("Length of the LIS: " + findLIS(arr));

                    
Length of the LIS: 5

Time Complexity:O(nlogn)
Auxiliary Space: O(nlogn)

Related Topic: Segment Tree

 



Last Updated : 24 Apr, 2023
Like Article
Save Article
Previous
Next
Share your thoughts in the comments
Similar Reads