求非连续最长单调递增子序列
简述:
设计一个O(n2)以及O(nlogn)时间的算法,找出n个数组成的序列的最长单调递增子序列
例如,
4, 5, 8, 8, 4, 0, 8, 3, 6, 9, 这十个数字
其中最长递增子序列为 4,5,8,9
O(n2)的算法(找子问题)
这个算法的角度是,遍历一遍原始数组,如果假设当前遍历的元素是第i个
那么只要考虑i号元素之前的最大递增子序列,如果该子序列最后一个元素result[curLongestLength - 1] < array[i] 那么自动把array[i] 加入到变更后的最长递增子序列
由于每一次判断都要遍历i号元素之前所有的j < i ,从而获取i之前的最长递增子序列所以时间复杂度为O(n2)
O(nlogn)的算法(在二分查找替换上的优化)
演示一下这个算法,
原始数组:
100, 102, 1, 101 , 2, 3, 4
用resultArray[7]保留结果
1) resultArray[0] = 100; int curLongestLength = 1;
2) 之后遍历到102元素,发现102大于resultArray最大的元素100, 自动在有序结果集中增加resultArray[++curLongestLength - 1] = 102;
3) 之后遍历到1,发现1小于等于resultArray[0]即结果集中最小的元素, 自动将resultArray[0] = 1;
4) 之后遍历到101, 发现这个数处于结果集最小值1 与 最大值102之间,于是用二分查找得到索引, 于是替换resultArray[1] = 101;
此时结果集resultArray为: resultArray[0] = 1, resultArray[1] = 101
5) 之后遍历到2,同4)得到索引为, 于是resultArray[1] = 2;
6) 之后遍历到3, 由于3 大于结果集中最大的元素2, 于是自动执行resultArray[++curLongestLength - 1] = 3;此时结果集总长度为3
7)之后遍历到4, 由于4 大于结果集中最大的元素3, 于是自动执行resultArray[++curLongestLength - 1] = 4;此时结果集总长度为4
于是得到最终的最长递增子序列长度为 4 , 其中元素为 1,2,3,4
两种算法代码实现:
package dynamic_programming; import java.util.Random; public class LongestIncrementalSubSequence { public static void main(String[] args) { int array[] = new int[10]; System.out.print("Raw Input: "); for(int i = 0;i < 10;i++){ Random random = new Random(); array[i] = random.nextInt(10); System.out.print(array[i] + ", " ); } System.out.println("\nLongest Distance of O(n2): " + GetLongestIncrementalSubSequence__O_N2_(array)); System.out.println("Longest Distance of O(nlogn): " + GetLongestIncrementalSubSequence__O_NLogN_(array)); } public static int GetLongestIncrementalSubSequence__O_N2_(int array[]){ int recordLengthArray[] = new int[array.length]; recordLengthArray[0] = 1; for(int i = 1;i < array.length;i++){ //use curLongestSubSequenceLength to record the length of //the longest sub incremental sequence before i int curLongestSubSequenceLength = 0; for(int j = 0;j < i;j++){ if(array[j] < array[i] && curLongestSubSequenceLength < recordLengthArray[j]){ curLongestSubSequenceLength = recordLengthArray[j]; } } recordLengthArray[i] = ++curLongestSubSequenceLength; } int longestIncermentalSubSequenceLength = 0; for(int i = 0;i < recordLengthArray.length;i++){ if(recordLengthArray[i] > longestIncermentalSubSequenceLength){ longestIncermentalSubSequenceLength = recordLengthArray[i]; } } return longestIncermentalSubSequenceLength; } public static int GetLongestIncrementalSubSequence__O_NLogN_(int array[]){ //use the result array to get the longest length int resultArray[] = new int[array.length]; if(array.length == 0) return 0; resultArray[0] = array[0]; int curLongestLength = 1; for(int i = 1;i < array.length;i++){ if(array[i] > resultArray[curLongestLength - 1]){ resultArray[++curLongestLength - 1] = array[i]; } else{ if(array[i] <= resultArray[0]){ resultArray[0] = array[i]; continue; } //use binary search to fill the correct position int start = 0; //point to b[0] int end = curLongestLength - 1; //point to b[curLongestLength - 1] for(;start != end - 1;){ if(array[i] > resultArray[(start + end) / 2]) start = (start + end) / 2; else end = (start + end) / 2; } resultArray[end] = array[i]; } } return curLongestLength; } }
输出: