算法————设计一个O(N^2)时间复杂度的算法,找出由n个数组成序列的最长单调递增序列

关于这道题,其实就是利用动态规划的一种思想,解这道题有点类似冒泡,采用遍历数组序列,不断更新递增序列的长度以至于来找到最长单调递增序列。
关于问题呢,大概已有有一点思路:
下面将思路画一画,通过将思想的整理,得到这样一副图:

这里写图片描述
通过图片我们可以看出要想找到最长递增子序列,我们得不断扩大范围这里是指图片中的(sub[i]所对应的区间),其实也就在小区间得到递增的序列的长度,然后继续扩大区间来更序列长度,更新到最大区间时就会找到一个最大的递增子序列。

先画一画然后得出这样的解决方案,然后就是调整代码实现这一过程便是找到最优解的方案。
以下是实现代码:

mian()函数:

#include<iostream>
using namespace std;
#include<vector>
#define MAX(a,b) a>b?a:b
int main()
{
    vector<int> LIS;//存储最长子序列
    int father[10] = { 6, 7, 5, 6, 7, 8, 9, 10, 1,3 };//n元素的数组
    int n = sizeof(father) / sizeof(father[0]);//数组大小
    int sub[10] = { 0 };//0到{0,1,2,3,4,5,,,n}区间的子序列长度
    getSLIS(father, sub,LIS,n);//获取子序列长度
    int lensub = 0;
    cout << "以下是每个区间更新得到子序列长度:" << endl;
    for (int i = 0; i < n; i++)//遍历sub,找出最大子序列长度
    {
        cout << sub[i] << " ";//打印更于sub中的子序列长度
        lensub = MAX(lensub, sub[i]);//选出最长的子序列
    }
    cout << endl;
    cout << "输出子序列长度:" << endl;
    cout << lensub << endl;
    cout << "输出子序列:" << endl;
    for (size_t i = 0; i < LIS.size(); i++)//输出子序列
    {
        cout << LIS[i] << " ";
    }
    cout << endl;
}

核心算法逻辑 getSLIS()函数

void getSLIS(int* f, int* s, vector<int>& LIS, int n)
{
    for (int i = 0; i < n; i++)
    {
        s[i] = 1;//初始化每个子序列长度为1
        for (int j = 0; j < i; j++)
        {
            int k = 0;
            if (f[i]>f[j] && s[i] + 1>s[j])//更新条件
            {
                s[i] = s[j] + 1;//更新
                if (s[i] > LIS.size())
                {
                    LIS.resize(s[i]);
                    int length = s[i];
                    int index = i;
                    while (length)//从当前子序列最大元素倒着将子序列输入LIS,length控制输入长度
                    {
                        LIS[length - 1] = f[index];
                        length--;
                        index--;
                    }
                }
            }
        }
    }
}
这里我为了使整个过程看起来更明了,打印了很多东西,为了清楚的看到最长递增子序列还特意使用了vector来辅助完成,这样是代码看起来就有了不少不必要的代码,下面将核心的getSLIS()函数简洁的写出来:
void getSLIS(int* f, int* s, vector<int>& LIS, int n)
{
    for (int i = 0; i < n; i++)
    {
        s[i] = 1;//初始化每个子序列长度为1
        for (int j = 0; j < i; j++)
        {
            int k = 0;
            if (f[i]>f[j] && s[i] + 1>s[j])//更新条件
            {
                s[i] = s[j] + 1;//更新
            }
        }
    }
}
是不是看起来更舒服了,其实也就这摸点东西呢,sub[]用来存储当前访问的数组范围的子序列,若满足就更新,反之就只有当前位置这一个元素的序列(比如4,3,1序列,当访问到3时不更新,当访问到1时也不更新,只有下一位置满足条件时,才在前一位置的基础上进行更新),以至于递增序列连续时才一个个的更新,从而整个区间更新完成后,就能从这些更新的数据序列(sub)中找到最大子序列
#时间复杂度分析:
#扩大范围即就是for (int i = 0; i < n; i++)循环,更新即就是for (int j = 0; j < i; j++)循环,俩层循环嵌套时间复杂度为O(N*(N-1)(N-2)……1)= O(N^2),这是单纯的分析求最长单调递增子序列长度,至于上面关于一些打印方面耗费的效率问题,不在这个算法时间复杂度的考虑范围内。

猜你喜欢

转载自blog.csdn.net/fangxiaxin/article/details/80595797