您将获得一系列数字,您需要从给定输入中找到最长的增加子序列(不必连续)。
我找到了这个链接(Longest increasing subsequence on Wikipedia)但需要更多解释。
如果有人能帮我理解O(n log n)实现,那将非常有用。如果你能用一个例子解释算法,那将非常感激。
我也看到了其他帖子,我不明白的是: L = 0 对于i = 1,2,... n: 二进制搜索最大正j≤L,使得X [M [j]] < X [i](如果不存在这样的值,则设置j = 0) 上面的陈述,从哪里开始二元搜索?如何初始化M [],X []?
答案 0 :(得分:98)
一个更简单的问题是找到最长的增长子序列的长度。您可以首先专注于理解该问题。该算法的唯一区别是它不使用 P 数组。
x 是序列的输入,因此可以初始化为: x = [0,8,4,12,2,10,6,14,1,9,5,13,3,11,7,15]
m 跟踪到目前为止找到的每个长度的最佳子序列。最好的是具有最小结束值的那个(允许在其后添加更宽范围的值)。长度和结束值是每个子序列需要存储的唯一数据。
m 的每个元素代表一个子序列。对于 m [j] ,
L 是迄今为止发现的最长子序列的长度。 m 的第一个 L 值有效,其余的未初始化。 m 可以从第一个元素为0开始,其余元素未初始化。算法运行时 L 会增加, m 的初始化值也会增加。
这是一个示例运行。给出了每次迭代结束时的 x [i] 和 m (但是使用了序列的值而不是索引)。
每次迭代中的搜索都在寻找放置 x [i] 的位置。它应尽可能向右(获得最长的序列),并且大于其左边的值(因此它是一个递增的序列)。
0: m = [0, 0] - ([0] is a subsequence of length 1.)
8: m = [0, 0, 8] - (8 can be added after [0] to get a sequence of length 2.)
4: m = [0, 0, 4] - (4 is better than 8. This can be added after [0] instead.)
12: m = [0, 0, 4, 12] - (12 can be added after [...4])
2: m = [0, 0, 2, 12] - (2 can be added after [0] instead of 4.)
10: m = [0, 0, 2, 10]
6: m = [0, 0, 2, 6]
14: m = [0, 0, 2, 6, 14]
1: m = [0, 0, 1, 6, 14]
9: m = [0, 0, 1, 6, 9]
5: m = [0, 0, 1, 5, 9]
13: m = [0, 0, 1, 5, 9, 13]
3: m = [0, 0, 1, 3, 9, 13]
11: m = [0, 0, 1, 3, 9, 11]
7: m = [0, 0, 1, 3, 7, 11]
15: m = [0, 0, 1, 3, 7, 11, 15]
现在我们知道有一个长度为6的子序列,以15结尾。子序列中的实际值可以通过在循环期间将它们存储在 P 数组中找到。
检索最佳子序列:
P 将前一个元素存储在每个数字的最长子序列(作为x的索引)中,并随着算法的进展而更新。例如,当我们处理8时,我们知道它在0之后,因此在 P 中存储8在0之后的事实。您可以从最后一个数字向后工作,如链接列表,以获得整个序列。
因此,对于每个数字,我们都知道它之前的数字。要查找以7结尾的子序列,我们查看 P 并查看:
7 is after 3
3 is after 1
1 is after 0
所以我们有子序列[0,1,3,7]。
以7或15结尾的子序列共享一些数字:
15 is after 11
11 is after 9
9 is after 6
6 is after 2
2 is after 0
所以我们有子序列[0,2,6,9,11]和[0,2,6,9,11,15](增长最长的子序列)
答案 1 :(得分:4)
麻省理工学院网站给出了这个问题的最佳解释之一。 http://people.csail.mit.edu/bdean/6.046/dp/
我希望它能清除你所有的疑虑。
答案 2 :(得分:1)
基于FJB的回答,java实现:
public class Lis {
private static int[] findLis(int[] arr) {
int[] is = new int[arr.length];
int index = 0;
is[0] = index;
for (int i = 1; i < arr.length; i++) {
if (arr[i] < arr[is[index]]) {
for (int j = 0; j <= index; j++) {
if (arr[i] < arr[is[j]]) {
is[j] = i;
break;
}
}
} else if (arr[i] == arr[is[index]]) {
} else {
is[++index] = i;
}
}
int[] lis = new int[index + 1];
lis[index] = arr[is[index]];
for (int i = index - 1; i >= 0; i--) {
if (is[i] < is[i + 1]) {
lis[i] = arr[is[i]];
} else {
for (int j = is[i + 1] - 1; j >= 0; j--) {
if (arr[j] > arr[is[i]] && arr[j] < arr[is[i + 1]]) {
lis[i] = arr[j];
is[i] = j;
break;
}
}
}
}
return lis;
}
public static void main(String[] args) {
int[] arr = new int[] { 0, 8, 4, 12, 2, 10, 6, 14, 1, 9, 5, 13, 3, 11,
7, 15 };
for (int i : findLis(arr)) {
System.out.print(i + "-");
}
System.out.println();
arr = new int[] { 1, 9, 3, 8, 11, 4, 5, 6, 4, 19, 7, 1, 7 };
for (int i : findLis(arr)) {
System.out.print(i + "-");
}
System.out.println();
}
}
答案 3 :(得分:1)
以下是O(NLogN)增长最长的子序列实现:
// search for the index which can be replaced by the X. as the index can't be
//0 or end (because if 0 then replace in the findLIS() and if it's greater than the
//current maximum the just append)of the array "result" so most of the boundary
//conditions are not required.
public static int search(int[] result, int p, int r, int x)
{
if(p > r) return -1;
int q = (p+r)/2;
if(result[q] < x && result[q+1]>x)
{
return q+1;
}
else if(result[q] > x)
{
return search(result, p, q, x);
}
else
{
return search(result, q+1, r, x);
}
}
public static int findLIS(int[] a)
{
int[] result = new int[a.length];
result[0] = a[0];
int index = 0;
for(int i=1; i<a.length; i++)
{
int no = a[i];
if(no < result[0]) // replacing the min number
{
result[0] = no;
}
else if(no > result[index])//if the number is bigger then the current big then append
{
result[++index] = no;
}
else
{
int c = search(result, 0, index, no);
result[c] = no;
}
}
return index+1;
}
答案 4 :(得分:0)
基于@fgb的回答,我使用c ++实现了算法,以找到最长的严格增加的子序列。希望这会有所帮助。
M [i]是长度为i的序列的最后一个元素的索引,P [i]是序列中i的前一个元素的索引,用于打印整个序列。
main()用于运行简单的测试用例:{0,8,4,12,2,10,6,14,1,9,5,13,3,11,7,15}。
#include <vector>
using std::vector;
int LIS(const vector<int> &v) {
int size = v.size(), max_len = 1;
// M[i] is the index of the last element of the sequence whose length is i
int *M = new int[size];
// P[i] is the index of the previous element of i in the sequence, which is used to print the whole sequence
int *P = new int[size];
M[0] = 0; P[0] = -1;
for (int i = 1; i < size; ++i) {
if (v[i] > v[M[max_len - 1]]) {
M[max_len] = i;
P[i] = M[max_len - 1];
++max_len;
continue;
}
// Find the position to insert i using binary search
int lo = 0, hi = max_len - 1;
while (lo <= hi) {
int mid = lo + ((hi - lo) >> 1);
if (v[i] < v[M[mid]]) {
hi = mid - 1;
} else if (v[i] > v[M[mid]]) {
lo = mid + 1;
} else {
lo = mid;
break;
}
}
P[i] = P[M[lo]]; // Modify the previous pointer
M[lo] = i;
}
// Print the whole subsequence
int i = M[max_len - 1];
while (i >= 0) {
printf("%d ", v[i]);
i = P[i];
}
printf("\n");
delete[] M, delete[] P;
return max_len;
}
int main(int argc, char* argv[]) {
int data[] = {0, 8, 4, 12, 2, 10, 6, 14, 1, 9, 5, 13, 3, 11, 7, 15};
vector<int> v;
v.insert(v.end(), data, data + sizeof(data) / sizeof(int));
LIS(v);
return 0;
}
答案 5 :(得分:0)
晚会,但这是一个JavaScript实现与其他人一起.. :))
var findLongestSubsequence = function(array) {
var longestPartialSubsequences = [];
var longestSubsequenceOverAll = [];
for (var i = 0; i < array.length; i++) {
var valueAtI = array[i];
var subsequenceEndingAtI = [];
for (var j = 0; j < i; j++) {
var subsequenceEndingAtJ = longestPartialSubsequences[j];
var valueAtJ = array[j];
if (valueAtJ < valueAtI && subsequenceEndingAtJ.length > subsequenceEndingAtI.length) {
subsequenceEndingAtI = subsequenceEndingAtJ;
}
}
longestPartialSubsequences[i] = subsequenceEndingAtI.concat();
longestPartialSubsequences[i].push(valueAtI);
if (longestPartialSubsequences[i].length > longestSubsequenceOverAll.length) {
longestSubsequenceOverAll = longestPartialSubsequences[i];
}
}
return longestSubsequenceOverAll;
};
答案 6 :(得分:-7)
我们可以在两个2d数组中实现它 像序列一样
8 2 4
0 7 1
3 7 9
且LIS为0 - > 2 - &gt; 4 - &gt; 7 - &gt; 8 什么是这个
的算法