问题
给定一个长度为N的数组,找出一个最长的单调自增子序列(不一定连续,但是顺序不能乱)。例如:给定一个长度为6的数组A{5, 6, 7, 1, 2, 8},则其最长的单调递增子序列为{5,6,7,8},长度为4.
int LIS(int nums[], int n, int result[]){//暴力法,时间O(n^2)
int* lis = new int[n];
int* pre = new int[n];
int maxlen = 1, maxIndex = 0;
lis[0] = 1;
memset(pre, -1, n * sizeof(int));//此处不能用sizeof(pre),这是因为pre是指针,sizeof(pre)为4,并非数组的大小。
for(int i = 1; i < n; i++){
lis[i] = 1;//将int型的lis数组全初始化为1,不能用memset(lis, 1, n * sizeof(int)),这样赋值会使初值为00000001_00000001_00000001_00000001,为16843009。
//memset是按字节赋值,因此只能全部赋值为0, 或者全部是f,即-1。
for(int j = 0; j < i; j++)
if(nums[i] > nums[j] && (lis[j] + 1) > lis[i]){
lis[i] = lis[j] + 1;
pre[i] = j;
}
if(lis[i] > maxlen){
maxlen = lis[i];
maxIndex = i;
}
}
int i = maxlen - 1;
while(maxIndex != -1){
result[i--] = nums[maxIndex];
maxIndex = pre[maxIndex];
}
delete [] lis, delete [] pre;
return maxlen;
}
int LIS2(int nums[], int n, int result[]){//采用二分查找,时间复杂度O(nlogn)
int maxlen = 1, maxIndex = 0;
int* maxNums = new int[n];//构建maxNums数组,其中maxNums[i]存储的是所有长度为i+1的最长递增子序列的最后一个元素的最小值
int* index = new int[n];//index数组记录maxNums数组中对应元素在原nums数组中的秩
int* pre = new int[n];//pre数组记录nums数组中每一元素的前驱的秩
memset(pre, -1, n * sizeof(int));
memset(index, 0, n * sizeof(int));
maxNums[0] = nums[0];
for(int i = 1; i < n; i++){//利用二分查找,在maxNums数组中查找nums[i]
int lo = 0, hi = maxlen;
while(lo < hi){
int mi = (lo + hi) >> 1;
if(maxNums[mi] < nums[i])
lo = mi + 1;
else
hi = mi;
}//求出的lo为大于nums[i]的元素的最小秩。参考数据结构(C++第3版)P56
if(lo == maxlen){//在maxNums中未找到nums[i],说明nums[i]大于maxNums中的最大值,则将此值插入maxNums中,maxlen长度加一。
maxNums[maxlen] = nums[i];
index[maxlen] = i;
pre[i] = index[maxlen - 1];//当前元素的前驱的秩即为长度为maxlen - 1的最长递增子序列的最后一个元素的秩
maxlen++;
maxIndex = i;
}
else{
maxNums[lo] = nums[i];//在maxNums中找到了nums[i],说明nums[i]小于原来位置的值,直接将原来位置的值替换
index[lo] = i;
pre[i] = (lo == 0) ? -1 : index[lo - 1];//当前元素的前驱的秩即为长度为lo - 1的最长递增子序列的最后一个元素的秩
}
}
int j = maxlen - 1;
while(maxIndex != -1){
result[j--] = nums[maxIndex];
maxIndex = pre[maxIndex];
}
delete [] maxNums, delete [] pre, delete [] index;
return maxlen;
}