最长递增子序列(longest increase sequence LIS)是各种面试笔试中经常考的一种题目,相似题目也有最长递减子序列,最长递增后递减序列。本文将详细介绍几种方法,供大家参考,不足之处,敬请原谅。
最长递增子序列即求解一组数值中最长递增序列长度,如有这样一个数组:{1, 3,5,7, 2, 9},那么这个数组的最长递增子序列就是5,即1, 3, 5, 7,9。
方法一:DP(动态规划)
像LCS一样,从后向前分析,很容易想到,第i个元素之前的最长递增子序列的长度要么是1(单独成一个序列),要么就是第i-1个元素之前的最长递增子序列加1,可以有状态方程:
LIS[i] = max{1,LIS[k]+1},其中,对于任意的k<=i-1,arr[i] > arr[k],这样arr[i]才能在arr[k]的基础上构成一个新的递增子序列。
int dp[31]; /* dp[i]记录到[0,i]数组的LIS,本例中假设arr的长度为31 */ int lis; /* LIS 长度 */ int LIS(int * arr, int size) { for(int i = 0; i < size; ++i) { dp[i] = 1; for(int j = 0; j < i; ++j) { if(arr[i] > arr[j] && dp[i] < dp[j] + 1) { dp[i] = dp[j] + 1; if(dp[i] > lis) { lis = dp[i]; } } } } return lis; }
方法二:排序+LCS(最长公共子序列:可参考:http://blog.csdn.net/v_JULY_v/article/details/6110269)
排序算法自己任意算法,LCS算法也是采用动态规划的思想去解决的。下面提供快速排序加LCS的代码:
方法三:DP+二分查找
void qsort(int * arr, int left, int right) { if(left >= right) return ; int index = left; for(int i = left+1; i <= right; ++i) { if(arr[i] < arr[left]) { swap(arr,++index,i); } } swap(arr,index,left); qsort(arr,left,index-1); qsort(arr,index+1,right); } int dp[31][31]; int LCS(int * arr, int * arrcopy, int len) { for(int i = 1; i <= len; ++i) { for(int j = 1; j <= len; ++j) { if(arr[i-1] == arrcopy[j-1]) { dp[i][j] = dp[i-1][j-1] + 1; }else if(dp[i-1][j] > dp[i][j-1]) { dp[i][j] = dp[i-1][j]; }else { dp[i][j] = dp[i][j-1]; } } } return dp[len][len]; }
维护一个数组MaxV[i],记录长度为i的递增子序列中最大元素的最小值(即可能存在n个长度为k的递增子序列,n个递增序列相应会有n个最大值M[n],MaxV记录M[n]中的最小值),并对于数组中的每个元素考察其是哪个子序列的最大元素,二分更新MaxV数组,最终i的值便是最长递增子序列的长度。这个方法真是太巧妙了,妙不可言。
/* 返回MaxV[i]中刚刚大于x的那个元素的下标 */ int BinSearch(int * MaxV, int size, int x) { int left = 0, right = size-1; while(left <= right) { int mid = (left + right) / 2; if(MaxV[mid] <= x) { left = mid + 1; }else { right = mid - 1; } } return left; } int LIS(int * arr, int size) { MaxV[0] = arr[0]; /* 初始化 */ len = 1; for(int i = 1; i < size; ++i) /* 寻找arr[i]属于哪个长度LIS的最大元素 */ { if(arr[i] > MaxV[len-1]) /* 大于最大的自然无需查找,否则二分查其位置 */ { MaxV[len++] = arr[i]; }else { int pos = BinSearch(MaxV,len,arr[i]); MaxV[pos] = arr[i]; } } return len; }
参考:
- http://blog.csdn.net/dlutbrucezhang/article/details/40143227
- http://blog.csdn.net/v_JULY_v/article/details/6110269
- http://www.ahathinking.com/archives/117.html