`
gaotong1991
  • 浏览: 94266 次
  • 来自: 北京
社区版块
存档分类
最新评论

最长上升子序列长度(LIS)-O(nlogn)算法

阅读更多
 

 此前在动态规划一讲:动态规划(3)-最长递增子序列 曾说过此问题,当前是的双重循环是O(n^2)的复杂度。

后来在网上看到说LIS问题有O(nlogn)的算法,于是拿来小研究了一下。

这个算法其实已经不是DP了,有点像贪心。至于复杂度降低其实是因为这个算法里面用到了二分搜索。本来有N个数要处理是O(n),每次计算要查找N次还是O(n),一共就是O(n^2);现在搜索换成了O(logn)的二分搜索,总的复杂度就变为O(nlogn)了。

这个算法的具体操作如下(by RyanWang):

开一个栈,每次取栈顶元素top和读到的元素temp做比较,如果temp > top 则将temp入栈;如果temp < top则二分查找栈中的比temp大的第1个数,并用temp替换它。 最长序列长度即为栈的大小top。

这也是很好理解的,对于x和y,如果x < y且Stack[y] < Stack[x],用Stack[x]替换Stack[y],此时的最长序列长度没有改变但序列Q的”潜力”增大了。

举例:原序列为1,5,8,3,6,7

栈为1,5,8,此时读到3,用3替换5,得到1,3,8; 再读6,用6替换8,得到1,3,6;再读7,得到最终栈为1,3,6,7。最长递增子序列为长度4。

用该算法完成POJ2533的具体代码如下:

 

#include <iostream>
#define SIZE 1001

using namespace std;

int main()
{
    int i, j, n, top, temp;
    int stack[SIZE];
    cin >> n;

    top = 0;
    /* 第一个元素可能为0 */
    stack[0] = -1;
    for (i = 0; i < n; i++)
    {
        cin >> temp;
        /* 比栈顶元素大数就入栈 */
        if (temp > stack[top])
        {
            stack[++top] = temp;
        }
        else
        {
            int low = 1, high = top;
            int mid;
            /* 二分检索栈中比temp大的第一个数 */
            while(low <= high)
            {
                mid = (low + high) / 2;
                if (temp > stack[mid])
                {
                    low = mid + 1;
                }
                else
                {
                    high = mid - 1;
                }
            }
            /* 用temp替换 */
            stack[low] = temp;
        }
    }

    /* 最长序列数就是栈的大小 */
    cout << top << endl;

    //system("pause");
    return 0;
}

 

这其中用到了二分查找第一个大于等于的,其实C++里面的有一个函数可用代替二分。

lower_bound 函数

下面是使用lower_bound优化最长上升子序列。由于长度相同的上升子序列只需要保存结尾最小的那个,而长度递增时,结尾数字的大小也是递增的。最长上升子序列就是找出比他大的第一个数。前面的数都比他小,所以他和这个数的长度相同。然后由于他比较然后小,更新找到的那个值。

#include<stdio.h>
#include<string.h>
#include<algorithm>

using namespace std;

int num[10]={3,6,3,2,4,6,7,5,4,3};

const int INF=0x3f3f3f3f;
int l=10;
int g[100];
int d[100];
int main()
{
	fill(g,g+l,INF);
	int max_=-1;
	for(int i=0;i<l;i++)
	{
		int j=lower_bound(g,g+l,num[i])-g;
		d[i]=j+1;
		if(max_<d[i])
			max_=d[i];
		g[j]=num[i];
	}
	printf("%d\n",max_);
	return 0;
}
 此前在动态规划一讲:动态规划(3)-最长递增子序列 曾说过此问题,当前是的双重循环是O(n^2)的复杂度。

后来在网上看到说LIS问题有O(nlogn)的算法,于是拿来小研究了一下。

这个算法其实已经不是DP了,有点像贪心。至于复杂度降低其实是因为这个算法里面用到了二分搜索。本来有N个数要处理是O(n),每次计算要查找N次还是O(n),一共就是O(n^2);现在搜索换成了O(logn)的二分搜索,总的复杂度就变为O(nlogn)了。

这个算法的具体操作如下(by RyanWang):

开一个栈,每次取栈顶元素top和读到的元素temp做比较,如果temp > top 则将temp入栈;如果temp < top则二分查找栈中的比temp大的第1个数,并用temp替换它。 最长序列长度即为栈的大小top。

这也是很好理解的,对于x和y,如果x < y且Stack[y] < Stack[x],用Stack[x]替换Stack[y],此时的最长序列长度没有改变但序列Q的”潜力”增大了。

举例:原序列为1,5,8,3,6,7

栈为1,5,8,此时读到3,用3替换5,得到1,3,8; 再读6,用6替换8,得到1,3,6;再读7,得到最终栈为1,3,6,7。最长递增子序列为长度4。

用该算法完成POJ2533的具体代码如下:

 

#include <iostream>
#define SIZE 1001

using namespace std;

int main()
{
    int i, j, n, top, temp;
    int stack[SIZE];
    cin >> n;

    top = 0;
    /* 第一个元素可能为0 */
    stack[0] = -1;
    for (i = 0; i < n; i++)
    {
        cin >> temp;
        /* 比栈顶元素大数就入栈 */
        if (temp > stack[top])
        {
            stack[++top] = temp;
        }
        else
        {
            int low = 1, high = top;
            int mid;
            /* 二分检索栈中比temp大的第一个数 */
            while(low <= high)
            {
                mid = (low + high) / 2;
                if (temp > stack[mid])
                {
                    low = mid + 1;
                }
                else
                {
                    high = mid - 1;
                }
            }
            /* 用temp替换 */
            stack[low] = temp;
        }
    }

    /* 最长序列数就是栈的大小 */
    cout << top << endl;

    //system("pause");
    return 0;
}

 

 

分享到:
评论

相关推荐

    对于一个数字序列,请设计一个复杂度为O(nlogn)的算法,返回该序列的最长上升子序列的长度,这里的子序列定义为这样一个序列U1,U2...,其中Ui < Ui+1,且AUi < AUi+1

    在算法领域,最长上升子序列(Longest Increasing Subsequence,简称LIS)是一个经典问题,它涉及到找出一个给定序列中严格单调递增的子序列,并返回这个子序列的最大长度。LIS问题不仅是计算机科学中的一个基础问题...

    leetcode-master-最长上升子序列

    最终,dp数组中的最大值即为整个序列的最长上升子序列长度。 LeetCode上的这个问题通常以题目形式出现,用户需要提交代码来解决这个问题。题目编号可能是20060w50,这可能是一个模拟编号,具体的编号会因LeetCode的...

    C语言:基于c代码实现的最长上升子序列

    优化的关键在于,在更新最长上升子序列长度的同时,记录该长度所对应的序列的最后一个元素的位置。使用一个辅助数组来维护长度为`i`的上升子序列的最小末尾值,并对每个元素进行二分查找,这样可以得到一个时间...

    最长递增子序列C程序

    最长递增子序列(Longest Increasing Subsequence, LIS)问题是一个经典的计算机科学问题,它在动态规划、算法设计和序列分析等领域都有广泛的应用。在这个C程序中,我们将深入探讨如何利用C语言来解决这个问题。 ...

    lis.rar_lis

    描述中的 "求一组数的最长上升子序列,时间复杂度为O(nlogn)" 提到,我们需要找到一种高效的方法来解决这个问题,而这个方法的时间复杂度应该是线性对数级别的。这意味着我们不能简单地采用暴力枚举所有可能的子序列...

    DP、二分-LeetCode300. 最长上升子序列(Python)

    在LeetCode的第300题“最长上升子序列”中,目标是找出给定无序整数数组中的最长上升子序列(Longest Increasing Subsequence, LIS)的长度。这是一道经典的动态规划(Dynamic Programming, DP)和二分查找(Binary ...

    数据与算法——实验报告——数列递增子序列

    在数据与算法的领域中,解决数列的最长递增子序列问题一直是一个核心议题。它不仅在理论上具有重要的地位,更在实际应用中发挥着关键作用。在EE课程中,我们通过实验报告的形式,深入探讨了如何通过动态规划方法来...

    算法艺术与信息学竞赛标准PPT学习教案.pptx

    4. **最长上升子序列 (Longest Increasing Subsequence, LIS)** - LIS是给定序列中最长的子序列,其元素按递增顺序排列。 - 通过动态规划可以解决此问题,时间复杂度为O(nlogn)。 5. **最优三角剖分** - 在给定...

    算法设计与分析习题答案 .pdf

    动态规划的一个经典例子是计算最长递增子序列(LIS),通过维护一个数组来记录到达每个位置的最长递增子序列的长度,最后通过构建最优解来得到最终结果。 贪心算法是一种在每一步选择中都采取在当前状态下最好或...

    快手2020招聘秋招笔试--工程B试卷.docx

    10. 最长递增子序列(LIS):LIS问题可以通过动态规划解决,时间复杂度为O(NlogN),空间复杂度为O(N)。答案是D。 11. 函数复杂度:函数F(n)的时间复杂度由递归公式决定,这里是O(n^2)。答案是D。 12. 整型计算:...

Global site tag (gtag.js) - Google Analytics