编程之美--2.16求数组中最长递增子序列
写一个时间复杂度尽可能低的程序,求一个一维数组(N个元素)中的最长递增子序列的长度。
例如:在序列1,-1,2,-3,4,-5,6,-7中,其最长的递增子序列为1,2,4,6
解法1:
遍历每个可能的子序列,判断是否为递增的。
//采用循环的方法,时间复杂度为O(N^2) public static int LIS(int[] arr){ int len = arr.length; int[] lenarr = new int[len]; int tempi ; for(int i = 0 ; i < len ; i++){ tempi = i ; lenarr[i]=1; for(int j = i+1 ; j < len ; j++){ if(arr[tempi]<arr[j]){//后面的元素比当前元素大 lenarr[i]++; tempi = j; } } } //输出递增长度数组 MyPrint(lenarr); int maxlen = GetMax(lenarr); return maxlen; }
解法2:类似解法1,比较的顺序不同
public static int LIS2(int[] arr){ int len = arr.length; int[] lenarr = new int[len]; for(int i = 0 ; i < len ; i++){ lenarr[i]=1; for(int j = 0 ; j < i ; j++){ if(arr[j]<arr[i]&&lenarr[j]+1 >lenarr[i]){ lenarr[i] = lenarr[j]+1; } } } MyPrint(lenarr); int maxlen = GetMax(lenarr); return maxlen; }
解法3:
在前面的分析中,当考察第i+1个元素的时候,我们是不考虑前面i个元素的分布情况的。现在我们从另一个角度分析,即当考察第i+1个元素的时候考虑前面i个元素的情况。
对于前面i个元素的任何一个递增子序列,如果这个子序列的最大的元素比array[i+1]小,那么就可以将array[i+1]加在这个子序列后面,构成一个新的递增子序列。
比如当i=4的时候,目标序列为1,-1,2,-3,4,-5,6,-7最长递增序列为(1,2),(-1,2)。
那么,只要4>2,就可以把4直接增加到前面的子序列中形成一个新的递增子序列。
因此,我们希望找到前i个元素中的一个递增子序列,使得这个递增子序列的最大的元素比array[i+1]小,且长度尽量地长。这样将array[i+1]加在该递增子序列后,便可以找到以array[i+1]为最大元素的最长递增子序列。
仍然假设在数组的前i个元素中,以array[i]为最大元素的最长递增子序列的长度为LIS[i]。
同时,假设:
长度为1的递增子序列最大元素的最小值为MaxV[1];
长度为2的递增子序列最大元素的最小值为MaxV[2];
……
长度为LIS[i]的递增子序列最大元素的最小值为MaxV[LIS[i]];
public static int LIS3(int[] arr){ int len = arr.length; //记录数组中的递增序列信息 //长度为1的递增子序列最大元素最小可以为MaxV[1] //长度为2的递增子序列最大元素最小可以为MaxV[2] int[] MaxV = new int[len]; MaxV[1] = arr[0]; //数组中的第一值,边界值 MaxV[0] = GetMin(arr)-1; //数组中的最小值,边界值 int[] lenarr = new int[len]; //以array[i]为最大元素的最长递增序列的长度为LIS[i] for(int i = 0 ; i < len ; i++){ lenarr[i]=1; } int maxlen = 1; for(int i = 1 ; i < len ; i++){ //遍历历史最长递增序列信息 int j ; for(j=maxlen ; j>=0 ; j--){ if(arr[i]>MaxV[j]){ //如果arr[i]大于长度为j的递增序列中的最大元素 lenarr[i]=j+1; //递增序列长度+1 break; } } //如果当前最长序列的长度大于最长递增序列长度,更新最长信息 if(lenarr[i] > maxlen){ maxlen = lenarr[i]; MaxV[lenarr[i]] = arr[i]; } //如果长度为j的递增序列中的最大元素小于arr[i]并且arr[i]<长度为j+1的递增序列中的最大元素 //则更新长度为j+1的递增序列中的最大元素为arr[i] else if(MaxV[j]< arr[i]&&arr[i]<MaxV[j+1]){ MaxV[j+1]= arr[i]; } } MyPrint(lenarr); return maxlen; } //注意:可将内层循环改成下面的形式 // for(j= lenarr[i-1] ; j >=1 ; j--){ // if(arr[i]>MaxV[j]){ // lenarr[i]=j+1; // break // } // }
辅助函数:
//获取数组最大值 public static int GetMax(int[] arr){ int len = arr.length; int maxlen = arr[0]; for(int i = 0 ; i < len ;i++){ if(arr[i]>maxlen) maxlen = arr[i]; } return maxlen; } //获取数组最小值 public static int GetMin(int[] lenarr){ int len = lenarr.length; int min = lenarr[0]; for(int i = 0 ; i < len ;i++){ if(lenarr[i]<min) min = lenarr[i]; } return min; } public static void MyPrint(int[] lenarr){ for(int i = 0 ; i <lenarr.length ; i++){ System.out.print(lenarr[i]); } System.out.println(); }
main函数:
public static void main(String[] args) { int[] arr = {1,-1,2,-3,4,-5,6,-7}; System.out.println("解法1:最长递增数组长度为:"+LIS(arr)); System.out.println("解法2:最长递增数组长度为:"+LIS2(arr)); System.out.println("解法3:最长递增数组长度为:"+LIS3(arr)); }
结果:
44332211
解法1:最长递增数组长度为:4
11213141
解法2:最长递增数组长度为:4
11213141
解法3:最长递增数组长度为:4
注意三种方法的lenarr是不同的,原因是比较的顺序不同

浙公网安备 33010602011771号