算法分析——单调递增子序列

题目描述

设计一个算法,找出由n个数组成的序列的最长单调递增子序列并对算法进行分析。要求时间复杂度分别为O(nlogn)和O(n^2).

方法1

对数组进行遍历,我们设置一个计数器来统计递增序列的长度并初始化为1(最短的递增序列是一个单独的数组,因此初始化为1)每次如果后一个数>前一个数,则计数器+1,否则将计数器置为1。当每次碰到递减的数时,更新最大计数器,然后再将计数器置为1.

代码:

#include <iostream>
#include <vector>
#include <cstdio>
using namespace std;
int main()
{
	//数组的长度
	int n;
	cin>>n;
	//数组元素
	int number;
	vector<int>arr;
	for(int i = 0;i < n;i++)
	{
		cin>>number;
		arr.push_back(number);
	}
	count = 1;
	max_count = 0;
	for(int i = 1;i < n;i++)
	{
		if(arr[i] < arr[i-1])
		{
			max_count = max(max_count,count);
			count = 1
		}
		else
			count += 1;
	}
	cout<<max_count;
	return 0;

方法1算法分析

方法1我们只需要遍历我们的数组,找到一个递增序列就+1即可,这是一次遍历,所以时间复杂度为O(n)

方法2

利用动态规划解决问题。我们发现其实截止到数组arr[i]的最长单调子序列长度其实就是之前的元素的最大子序列长度.我们不断更新当前元素之前的最大单调子序列长度,只是为了更新我们当前元素的最大长度。
所以我们设置b[0:n-1]来存储数组中a[i]的最大单调递增子序列长度,我们要做的就是不断更新b中的元素。

#include <iostream>
using namespace std;
const int maxn=100010;
int a[maxn]={0},b[maxn]={0};
int LIS(int a[],int n)
{
    int k;
    b[0]=1;
    for(int i=1;i<n;i++)
    {
        k=0;
        for(int j=0;j<i;j++)
        {
            if(a[j]<=a[i]&&k<b[j])//找到比a[i]小的数,取比a[i]小的数集合中的最大值更新
                k=b[j];
        }
        b[i]=k+1;
    }
    int res=0;
    for(int i=0;i<n;i++)
    {
        res=res<b[i]?b[i]:res;
    }
    return res;
}
int main()
{
    int n;
    cin>>n;
    for(int i=0;i<n;i++)
    {
        scanf("%d",&a[i]);//这里天坑!不用scanf和printf会超时       
    }
    cout<<printf("%d",LIS(a,n))
    return 0;
}

方法2算法分析

我们每次在找a[i]的最大递增序列时都会遍历之前的所有b[0:i],因此算法时间复杂度为O(n^2)

方法3

方法3是一种最为简便的方法,扩展序列。
我们发现在遍历到i的时候,前面所有b数组中的元素我们都需要遍历,因此这造成了很大程度的时间浪费。我们可以重新定义一个数组B,数组B[k]中存储的是长度为k的递增序列中最小元素末尾。所以当我们遍历到a[i]时需要比较:
· a[i]>=B[k],B[k++] = a[i]
· a[i]<B[k],我们在B数组中进行二分查找,因为数组B有序,我们查找到一个B[j-1]<=a[i]<B[j]的位置,将B[j]更新为a[i]

#include <iostream>
using namespace std;
const int maxnum = 100010;
int a[maxnum] = {0};
int b[maxnum] = {0};
int binarysearch(int x,int i ,int j)
{
	int l = i,r = j;
	while(l <= r)
	{
		mid = (l+r)//2;
		if(x==b[mid])
			return mid;
		else if(x<b[mid])
			r=mid-1;
		else
			l=mid+1;
	}
	return l;
}
int LIS(int a[],int n)
{
	int k = 1;
	b[1] = a[0];
	for(int i = 1;i < n;i++)
	{
		if(a[i]>=b[k])
			b[++k]=a[i];
		else
			b[binarysearch(a[i],1,k)]=a[i];
	}
	return k;
}
int main()
{
	int n;
	cin>>n;
	for(int i = 0;i < n;i++)
		cin>>a[i];
	cout<<LIS(a,n)<<endl;
	return 0;

方法3算法分析

b[k]为长度为k的递增序列的末尾最小元素,当碰到一个小于当前a[i]的数字,我们会进入b中进行位置搜索,搜到了位置j后我们会立刻用a[i]这个较小的元素更新b[j],因为原位置b[j]是大于a[i]的,所以我们要更新成更小的。算法时间复杂度为O(nlogn)

  • 2
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值