📜  最长递增子序列nlogn c++(1)

📅  最后修改于: 2023-12-03 15:40:17.450000             🧑  作者: Mango

最长递增子序列nlogn c++

简介

本文介绍了一种求解最长递增子序列(Longest Increasing Subsequence,LIS)的高效算法。该算法的时间复杂度为O(nlogn),比暴力枚举和动态规划算法效率更高。

问题描述

给定一个序列,从中选出若干个数,使得它们按照原序列中的顺序构成一个递增的序列,并且这个递增序列的长度尽可能长。例如,对于序列 {6, 2, 8, 5, 1, 7},其中一个最长递增子序列是 {2, 5, 7},长度为3。

算法思路

该算法的核心思想是维护一个长度逐渐增大的子序列,并在其中不断更新,使得该子序列的末尾元素尽可能的小。具体步骤如下:

  1. 建立一个空的数组b,用来存储当前最长递增子序列。令b[0]为原序列中的第一个数,令len为1表示这个最长递增子序列的长度。

  2. 从原序列中的第二个数开始遍历,对于每个数a[i],找到b中最后一个小于等于a[i]的数,令其下标为j。

  3. 如果b中不存在小于等于a[i]的数,则将a[i]添加到b的末尾,更新len。

  4. 否则,将b[j+1]替换为a[i],更新len。

很显然,每次替换后得到的b仍然是一个长度递增的递增子序列,且其中末尾元素尽量小。最终得到的b即为最长递增子序列。

代码实现

下面是C++代码实现,其中LIS()函数返回最长递增子序列的长度。

#include <iostream>
#include <algorithm>
using namespace std;

const int N = 100010;
int a[N], b[N], len;

int LIS()
{
    b[0] = a[0];
    len = 1;
    for (int i = 1; i < n; i++) {
        if (a[i] > b[len - 1]) {
            b[len++] = a[i];
        } else {
            int j = lower_bound(b, b + len, a[i]) - b;
            b[j] = a[i];
        }
    }
    return len;
}

int main()
{
    int n;
    cin >> n;
    for (int i = 0; i < n; i++) {
        cin >> a[i];
    }
    cout << LIS() << endl;
    return 0;
}
时间复杂度

LIS()函数中循环体内部只进行了一次二分查找操作,二分查找的时间复杂度为O(logn),因此总时间复杂度为O(nlogn)。

总结

本文介绍了最长递增子序列问题,并给出了一种时间复杂度为O(nlogn)的高效算法。对于需要求解最长递增子序列的问题,在数据范围合理的情况下,可以优先考虑该算法。