Author: 2018013402 [email protected]
转载请注明出处:https://www.jianshu.com/p/a670c3da1ff8
实验环境:
OS:Windows 10 家庭中文版 x64
CPU:Intel(R) Core(TM) i7- 8750H CPU @ 2.20 GHz
RAM: 16.0 GB
IDE:Visual Studio 2019 Enterprise
算法分析/实验设计思路:
使用编程语言:C++
长度为的递增子序列的最小尾元素一定小于长度为的递增子序列的最小尾元素。
用表示长度为的递增子序列的最小尾元素,由上面的结论知,必定是有序递增的。
从前至后扫描,扫描到时,若,则子序列长度增加,是子序列的最后一个元素;若,找到大于等于的最小,以替换。扫描完成后长度即是所求最长子序列的长度,但子序列并不是的序列。
-
考虑在过程中用大小为的二维数组记录每次读取后的。
从该二维数组的右下角开始,读取,最长递增子序列的第个元素为当前所在的数字,向上读取,如果数字相同则继续向上,直到数字不同,向左移动,记录新的第个元素,之后重复上述步骤,直到找到整个最长递增子序列。
但是发现这样会导致整体算法时间复杂度变成(更新二维数组的下半部分)。
考虑数组中所有元素在包含它的递增子序列的前一个元素是不会改变的,可以依据这个来记录每个元素的前驱元素来输出最后的最长递增子序列。可以得到时间复杂度为的LIS算法。
程序说明:
LIS.exe
输入:
手动输入数组S:
n 1
S[0] S[1] S[2] ... S[n-2] S[n-1]
随机生成数组S:
n 0
输出:最长递增子序列以及算法运行时间。
实验结果:
正确性验证:
输入:
9 1
2 1 3 0 4 1 5 2 7
1 3 4 5 7
running time: 0ms
输出:
8 1
6 7 8 2 3 4 5 10
2 3 4 5 10
running time: 0ms
时间复杂度验证:
输入规模n | 算法运行时间/ms |
---|---|
10,000 | 1 |
100,000 | 4 |
1,000,000 | 54 |
10,000,000 | 615 |
20,000,000 | 1268 |
40,000,000 | 2630 |
60,000,000 | 4044 |
80,000,000 | 5407 |
100,000,000 | 6819 |
可以发现,该算法时间复杂度大约是线性的,因为项的影响较小,运行时间比线性时间稍大。例如,时时间比时时间的十倍稍大;时时间比时时间的十倍稍大;时时间比时时间的两倍稍大;这些均表明该算法时间复杂度与理论的契合。
源代码如下:
#include
#include
#include
using namespace std;
void random_input(vector& S, int n)
{
for (int i = 0; i < n; ++i)
{
S[i] = rand();
}
}
int main() {
vector elem;
vector pos;
int n;
bool typing = false;
cin >> n >> typing;
vector S(n);
vector prev(n);
if (typing)
{
for (int i = 0; i < n; ++i)
{
int ele;
cin >> ele;
S.push_back(ele);
}
}
else
{
random_input(S, n);
}
clock_t start = clock();
for (int i = 0; i < n; ++i)
{
if (elem.empty()) // i == 0
{
elem.push_back(S[i]);
pos.push_back(i);
prev[0] = -1;
}
else if (S[i] > elem[elem.size() - 1])
{
elem.push_back(S[i]);
pos.push_back(i);
prev[i] = pos[pos.size() - 2];
}
else
{
int s = 0, t = elem.size() - 1, k = (s + t) / 2;
while (1)
{
if (elem[k] == S[i])
{
pos[k] = i;
prev[i] = pos[k - 1];
break;
}
else if (elem[k] > S[i] && k != 0)
{
t = k;
k = (s + t) / 2;
}
else if (elem[k] < S[i])
{
s = k;
k = (s + t) / 2;
if (k == s)
{
elem[k + 1] = S[i];
pos[k + 1] = i;
prev[i] = pos[k];
break;
}
}
else // k == 0
{
elem[k] = S[i];
pos[k] = i;
prev[i] = -1;
break;
}
}
}
}
vector ans(elem.size());
int flag = pos[elem.size() - 1];
for (int i = elem.size() - 1; flag != -1; --i)
{
ans[i] = S[flag];
flag = prev[flag];
}
clock_t end = clock();
for (auto it = ans.begin(); it != ans.end(); ++it)
{
cout << *it << " ";
}
cout << "\nrunning time: " << end - start << "ms\n";
return 0;
}
/*
* O(n*n) try
#include
#include
#include
using namespace std;
void random_input(vector& S, int n)
{
for (int i = 0; i < n; i++)
{
S[i] = rand();
}
}
void change(vector>& elem, int i, int k, int ele)
{
for (int j = i; j < elem.size(); ++j)
{
elem[j][k] = ele;
}
}
void push_back(vector>& elem, int i, int ele)
{
for (int j = i; j < elem.size(); ++j)
{
elem[j].push_back(ele);
}
}
int main() {
vector S;
int n;
cin >> n;
vector> elem(n);
for (int i = 0; i < n; ++i)
{
int ele;
cin >> ele;
S.push_back(ele);
}
clock_t start = clock();
for (int i = 0; i < n; ++i)
{
if (elem[i].empty() || S[i] > elem[i][elem[i].size() - 1])
{
push_back(elem, i, S[i]);
}
else
{
int s = 0, t = elem[i].size() - 1, k = (s + t) / 2;
while (1)
{
if (elem[i][k] == S[i])
{
break;
}
else if (elem[i][k] > S[i] && k != 0)
{
t = k;
k = (s + t) / 2;
}
else if (elem[i][k] < S[i])
{
s = k;
k = (s + t) / 2;
if (k == s)
{
change(elem, i, k + 1, S[i]);
break;
}
}
else // k == 0
{
change(elem, i, k, S[i]);
break;
}
}
}
}
int i = n - 1, j = elem[n - 1].size() - 1;
vector ans(j + 1);
while (1)
{
ans[j] = elem[i][j];
if (j == 0)
{
break;
}
while (1)
{
if (elem[i - 1].size() > j && elem[i - 1][j] == elem[i][j])
{
i = i - 1;
}
else
{
j = j - 1;
break;
}
}
}
clock_t end = clock();
for (auto it = ans.begin(); it != ans.end(); ++it)
{
cout << *it << " ";
}
cout << "\nrunning time: " << end - start << "ms\n";
return 0;
}
*/