昨天做爱奇艺笔试题,最后一道编程题是求整型数组最长递增子序列,由于时间关系,没有完全写出来,今天重新来做做这一系列题。
《1》 最大子数组之和
首先从最简单的最大子数组之和求取。数组里有正数、负数、零。设包含第 i 个元素的子数组的和为 Sum,则Sum的值为
Sum(i) = Sum(i-1) + arrey[i]; 显然如果arrey[i]<=0,则Sum(i)<=Sum(i-1);则必须把Sum(i)=arrey[i];同时maxSum用来保存Sum最大值。时间复杂度为o(n);
#include<iostream> #include<math.h> using namespace std; int max(int a,int b){ return a>b?a:b; } int FindGreatestSumOfSubArrey(int input[],int length){ int TempSum=input[0]; int MaxSum=TempSum; for (int i = 1;i < length; i++){ TempSum = max(TempSum + input[i],input[i]); MaxSum = max(TempSum,MaxSum); } return MaxSum; } int main(){ int input[] = {1,-2,-5,3,5}; cout<<FindGreatestSumOfSubArrey(input,5)<<endl;// 结果为8 return 0; }
上面题目中,如果要求返回和最大子数组,则可以设置两个指针begin,end来指向maxSum的第一个元素和最后一个元素。 设置指针first,last来指向当前Sum的第一个元素和最后一个元素。当Sum>=maxSum时,如果begin=first时,通过last来更新end,如果begin!=first,则设置begin=first,end=last. 当Sum<maxSum时,保持begin 和first值不变。
1 #include<iostream> 2 #include<math.h> 3 using namespace std; 4 int max(int a,int b){ 5 return a>b?a:b; 6 } 7 void FindGreatestSumOfSubArrey(int input[],int length){ 8 int TempSum=input[0]; 9 int MaxSum=TempSum; 10 int first,last; 11 int begin,end; 12 first = last = begin = end = 0; 13 for (int i = 1;i < length; i++){ 14 TempSum += input[i]; 15 if (TempSum <= input[i]){ 16 first = last = i; 17 TempSum = input[i]; 18 } 19 else 20 last = i; 21 if (MaxSum <= TempSum){ 22 MaxSum = TempSum; 23 begin = first; 24 end = last; 25 } 26 27 } 28 cout<<"MaxSum = "<<MaxSum<<endl; 29 for (i = begin; i <= end; i++) 30 cout<<input[i]<<" "; // 输出子数组 31 }
1 int main(){ 2 int test1[] = {1,-2,-5,3,5}; 3 int test2[]={1,1,1,1,1}; 4 int test3[]={-1,-1,-1,-1,-1}; 5 int test4[]={-1,2,-3,1,1}; 6 FindGreatestSumOfSubArrey(test1,5); 7 cout<<endl; 8 FindGreatestSumOfSubArrey(test2,5); 9 cout<<endl; 10 FindGreatestSumOfSubArrey(test3,5); 11 cout<<endl; 12 FindGreatestSumOfSubArrey(test4,5); 13 cout<<endl; 14 return 0; 15 }
对于第四组测试数据,其中有最大子数组之和有两个。一个{2},另一个是{1,1};上述代码得到的是后一种答案,有兴趣的可以把最大和相同的子数组全部输出。
《2》最大子数组之积
分别求取以第i个元素开始的数组的积,然后在求取所有积中最大值即可。时间复杂度为o(n^2)
1 #include <stdio.h> 2 int getmaxsub(int *input, int size){ 3 if(input == NULL || size == 0) return 0xFFFF; 4 if(size == 1) return input[0]; 5 int current_product; 6 int max = input[0]; 7 int first,last; 8 first = last = 0; 9 for(int len = 0;len<size;len++){ 10 current_product =input[len]; 11 for(int end = len + 1; end < size; end++){ 12 current_product *= input[end]; 13 if(current_product > max){ 14 first = len ; 15 last = end; 16 max = current_product; 17 } 18 } 19 } 20 for (int k = first; k <= last ;k++) 21 printf("%d ",input[k]); 22 printf(" "); 23 return max; 24 } 25 26 int main(){ 27 int input[] = {5,1,-2,4,9,1}; 28 printf("maxmult : %d ", getmaxsub(input,6)); 29 return 0; 30 }
动态规划做法,假设数组为a[N],max[N] 表示以下标为 i 结尾的子数组乘积最大值,min[N] 表示以下标为 i 结尾的子数组乘积最小值。为了处理数组元素为负的问题,必须将最小乘积也保存起来。很容易想到,若当前元素a[i]为负数,那么a[i]*max[i-1]得到的值并不一定比a[i]*min[i-1]大,因为min[i-1]可能为负,如果min[i-1]的绝对值大于max[i-1],那么a[i]*min[i-1]负负相乘的值是更大的,因此有转移方程:
max[i] = MaxinThree(a[i], a[i]*max[i-1], a[i]*min[i-1]); //求三者最大
min[ i] = MininThree(a[i], a[i]*max[i-1], a[i]*min[i-1]); //求三者最小
1 #include<stdio.h> 2 #include<iostream> 3 using namespace std; 4 int MaxinThree(int a, int b, int c) 5 { 6 return (((a>b)?a:b)>c) ? (a>b?a:b) : c; 7 } 8 int MininThree(int a, int b, int c) 9 { 10 return (((a<b)?a:b)<c) ? (a<b?a:b) : c; 11 } 12 void FindGreatestMultiOfSubArrey(int *input,int size){ 13 int *max = new int[size]; 14 int *min = new int[size]; 15 int product; 16 max[0] = min [0] = input[0]; 17 product = max[0]; 18 for (int i = 1; i < size; i++){ 19 max[i] = MaxinThree(input[i],input[i]*max[i-1],input[i]*min[i-1]); 20 min[i] = MininThree(input[i],input[i]*min[i-1],input[i]*max[i-1]); 21 if(max[i] > product) 22 product = max[i]; 23 } 24 cout<<product<<endl; 25 } 26 int main(){ 27 int input[] = {5,-1,-2,4,9,1}; 28 void FindGreatestMultiOfSubArrey(int *input,int size); 29 FindGreatestMultiOfSubArrey(input,6); 30 return 0; 31 32 }
《3》最长递增子序列
最长递增序列不要求数组元素连续问题,返回递增序列长度和递增序列。o(n^2)做法,顺序比较以第i个元素开头的递增序列即可。
1 #include<stdio.h> 2 #include<iostream> 3 using namespace std; 4 void FindGreatestAddOfSubArrey(int *input,int size){ 5 int *result = new int[size]; 6 int *pre = new int[size]; 7 int k,MaxLen = 0; 8 for (int len = 0; len < size; len++){ 9 int temp = input[len]; 10 int cnt = 0; 11 pre[0] = input[len]; 12 for(int end = len + 1; end < size; end++){ 13 if (input[end] > temp){ 14 temp = input[end]; 15 pre[++cnt] = temp; 16 } 17 } 18 if (cnt >= MaxLen){ 19 k = 0; 20 MaxLen = cnt; 21 while(k <= cnt){ 22 result[k] = pre[k]; 23 k++; 24 } 25 } 26 } 27 cout<<MaxLen+1<<endl; 28 for(int i = 0;i < k; i++) 29 cout<<result[i]<<" "; 30 cout<<endl; 31 } 32 33 int main(){ 34 int test1[] = {5,-1,-2,4,9,1}; 35 int test2[] = {1,2,3,4,5,6}; 36 int test3[] = {6,5,4,3,2,1}; 37 FindGreatestAddOfSubArrey(test1,6); 38 FindGreatestAddOfSubArrey(test2,6); 39 FindGreatestAddOfSubArrey(test3,6); 40 return 0; 41 }
利用动态规划来做,假设数组为1, -1, 2, -3, 4, -5, 6, -7。我们定义LIS[N]数组,其中LIS[i]用来表示以array[i]为最后一个元素的最长递增子序列。
使用i来表示当前遍历的位置:
当i = 0 时,显然,最长的递增序列为(1),则序列长度为1。则LIS[0] = 1
当i = 1 时,由于-1 < 1,因此,必须丢弃第一个值,然后重新建立序列。当前的递增子序列为(-1),长度为1。则LIS[1] = 1
当i = 2 时,由于2 > 1,2 > -1。因此,最长的递增子序列为(1, 2),(-1, 2),长度为2。则LIS[2] = 2。
当i = 3 时,由于-3 < 1, -1, 2。因此,必须丢掉前面的元素,重建建立序列。当前的递增子序列为(-3),长度为1。则LIS[3] = 1。
依次类推之后,可以得出如下结论。
LIS[i] = max{1, LIS[k] + 1}, array[i] >array[k], for any k < i
最后,我们取max{Lis[i]}。
1 #include<stdio.h> 2 #include<iostream> 3 using namespace std; 4 void FindLongestAscSequence(int *input,int size){ 5 int *list = new int[size];// 用来存储以第i个元素结尾的最长递增子序列 6 int MaxLen = 1; 7 int k = 0; 8 for (int i = 0; i < size; i++){ 9 list[i] = 1 ; 10 for ( int j = 0; j < i; j++){ 11 if ((input[i] > input[j]) && (list[j] +1 > list[i]) ) 12 list[i] = list[j] + 1; 13 } 14 if (MaxLen < list[i]){ 15 MaxLen = list[i]; 16 } 17 } 18 cout<<MaxLen<<endl; 19 } 20 21 int main(){ 22 int test1[] = {5,-1,-2,4,9,1}; 23 int test2[] = {1,2,3,4,5,6}; 24 int test3[] = {6,5,4,3,2,1}; 25 FindLongestAscSequence(test1,6); 26 FindLongestAscSequence(test2,6); 27 FindLongestAscSequence(test3,6); 28 return 0; 29 }
后续更新