编程之美2.16 最长递增子序列:
写一个时间复杂度尽可能低的程序,求一个一维数组(N个元素)中的最长递增子序列的长度。例如:在序列1,-1,2,-3,4,-5,6-7中,其最长的递增子序列为1,2,4,6
分析:利用动态规划分析。用A[i]来更新maxLen和MaxV:,从A[0]~A[i-1]可能存在多个存在多个子问题的最优解,所以需要将他他们合并为一个。
1. 当A[i]>MaxV[maxLen](即子问题最优解的最小值)时,maxLen++ && MaxV[maxLen] = a[i]
A[i]>MaxV[maxLen]时,A[i]必然比MaxV[0]-MaxV[maxLen]中所有的值都大,因此,比如不会更新这些已有的长度的尾部最小值
2. 当A[j]<A[i]<A[j+1]时,即存在一个更小的局部问题最优解,所以需要更新这个最优解。更新长度为j+1的记录,即:MaxV[j+1] =A[i]
1).对于MaxV[0]-MaxV[j]的不用说了,A[i]比他们都大,他们都不会更新了,
2).对于MaxV[j+1],刚好可以更新其值为A[i],
3).对于MaxV[j+2]-MaxV[MaxLen]的元素由于递增子序列的后无效性,也不需要更新。对于长度相同的最长递增子序列, 我们可以将他们分到同一组中(合并为一组),并只记录它们最大元素的最小值MaxV[长度值], 如: (1,3, 4, 6) 和(-4, -2, -1, 8), 可以合并为(-4, -2, -1,6)
就是这样了,分析好对于每个A[i],如何更新maxLen和MaxV就ok了。
// 用二分法查找是否存在 MaxV[k]<array[i]<MaxV[k+1] public static int BinarySearch(int[] array, int maxLength, int target) { int left = 0; int right = maxLength - 1; int middle = 0; while (left <= right) { middle = (left + right) / 2; if (array[middle] == target) { return middle; } else if (array[middle] < target) { left = middle + 1; } else { right = middle - 1; } } return array[middle] > target ? middle : middle + 1; } // 编程之美2.16 最长递增子序列 public static int GetMaxLength(int[] array) { int maxLen = 1; int[] MaxV = new int[array.Length]; MaxV[0] = array[0]; for (int i = 1; i < array.Length; i++) { if (array[i] > MaxV[maxLen - 1]) { maxLen++; MaxV[maxLen-1] = array[i]; } else //if last_min[k]<array[i]<last_min[k+1] { int pos = BinarySearch(MaxV,maxLen, array[i]); if (pos >= 0) //存在 MaxV[k]<array[i]<MaxV[k+1],更新 MaxV[k+1] { MaxV[pos] = array[i]; } } } return maxLen; }