现在的位置: 首页 > 综合 > 正文

每日一题(79) – 求数组中最长递增子序列

2013年10月05日 ⁄ 综合 ⁄ 共 3280字 ⁄ 字号 评论关闭

题目来自编程之美

题目


思路(1) 动态规划(复杂度为n^2)

方程:

F[i]:表示以nArr[i]为结尾的最长递增子序列的最大长度。
F[i] = Max(F[j]) + 1 && a[i] > a[j] && i > j;

方程的直观含义:检查nArr[i]能接在哪一个数的后面。

如果要求LIS序列,需要令开辟一个数组。

复杂度:O(n^2)

代码:只求LIS的长度

#include <iostream>
#include <assert.h>
using namespace std;

/*
F[i]:表示以nArr[i]为结尾的最长递增子序列的长度
F[i] = Max(F[j]) + 1 && a[i] > a[j] && i > j;
F[i] = 1
*/
int LIS(int nArr[],int nLen)
{
	int nMaxLen = 1;
	int* F = new int[nLen];
	//递推
	for (int i = 1;i < nLen;i++)
	{
		F[i] = 1;
		for (int j = i - 1;j >= 0;j--)
		{
			if (nArr[i] > nArr[j])
			{
				F[i] = max(F[i],F[j] + 1);
			}
		}
		nMaxLen = max(nMaxLen,F[i]);
	}
	//输出
	return nMaxLen;
}

int main()
{
	int nArr[8] = {1,-1,2,-3,4,-5,6,-7};
	cout<<LIS(nArr,8)<<endl;
	system("pause");
	return 1;
}

代码:输出一个LIS序列

#include <iostream>
#include <assert.h>
using namespace std;

void Print(int* nArr,int* Pre,int nCurPos)
{
	if (nCurPos == -1)
	{
		return;
	}
	Print(nArr,Pre,Pre[nCurPos]);
	cout<<nArr[nCurPos]<<" ";
}

/*
F[i]:表示以nArr[i]为结尾的最长递增子序列的长度
F[i] = Max(F[j]) + 1 && a[i] > a[j] && i > j;
F[i] = 1
*/
int LIS(int nArr[],int nLen)
{
	int nMaxLen = 1;
	int* F = new int[nLen];
	int* Pre = new int[nLen];
	int nMaxPos = -1;
	//递推
	for (int i = 1;i < nLen;i++)
	{
		F[i] = 1;
		Pre[i] = -1;
		for (int j = i - 1;j >= 0;j--)
		{
			if (nArr[i] > nArr[j])
			{
				if (F[i] < F[j] + 1)
				{
					F[i] = F[j] + 1;
					
					Pre[i] = j;
				}
			}
		}
		if (nMaxLen < F[i])
		{
			nMaxLen = F[i];
			nMaxPos = i;
		}
	}
	//输出
	Print(nArr,Pre,nMaxPos);
	cout<<endl;
	return nMaxLen;
}

int main()
{
	int nArr[8] = {1,-1,2,-3,4,-5,6,-7};
	cout<<LIS(nArr,8)<<endl;
	system("pause");
	return 1;
}

思路(2)O(nlogn)的算法

思路:数组nArrIncr中的数据都是不重复的。

代码:

int BinSearch(int nArrIncr[],int nNum,int nStart,int nEnd)
{
	int nMid = -1;
	while(nStart <= nEnd)
	{
		nMid = (nStart + nEnd) >> 1;
		if (nArrIncr[nMid] == nNum)
		{
			return nMid;//返回nNum的位置
		}
		else if (nArrIncr[nMid] > nNum)
		{
			nEnd = nMid - 1;
		}
		else
		{
			nStart = nMid + 1;
		}
	}
	return nStart; //返回第一个大于nNum的位置
}

int LIS(int nArr[],int nLen)
{
	assert(nLen > 0);
	int nEnd = 0;
	int* nArrIncr = new int[nLen];
	nArrIncr[0] = nArr[0];
	int nPos = -1;
	for (int i = 1;i < nLen;i++)
	{
		if (nArr[i] > nArrIncr[nEnd])
		{
			nArrIncr[++nEnd] = nArr[i];
		}
		else
		{
			nPos = BinSearch(nArrIncr,nArr[i],0,nEnd);//寻找第一个大于等于nArr[i]的数
			nArrIncr[nPos] = nArr[i];
		}
	}
	return nEnd + 1;
}

利用STL,给出一个更简洁的代码:

int LIS(int nArr[],int nLen)
{
	assert(nLen > 0);
	int nEnd = 0;
	int* nArrIncr = new int[nLen];
	nArrIncr[0] = nArr[0];
	int nPos = -1;
	for (int i = 1;i < nLen;i++)
	{
		if (nArr[i] > nArrIncr[nEnd])
		{
			nArrIncr[++nEnd] = nArr[i];
		}
		else
		{
			//寻找第一个大于等于nArr[i]的数
			*lower_bound(nArrIncr,nArrIncr + nEnd + 1,nArr[i]) = nArr[i];
		}
	}
	return nEnd + 1;
}

注:

lower_bound作用:在区间[first,last)进行二分查找,返回第一个大于或等于val的元素位置(迭代器)。

(1)如果所有元素都小于val,则返回last的位置,且last的位置是越界的

(2)头文件:#include <algorithm>

输出一个LIS,复杂度为O(n)

代码

#include <iostream>
#include <assert.h>
#include <algorithm>
using namespace std;

void Print(int nArr[],int nCurLen,int nCurPos,int nArrPos[])
{
	if (nCurLen == 0)
	{
		return;
	}
	if (nCurLen == nArrPos[nCurPos])
	{
		Print(nArr,nCurLen - 1,nCurPos - 1,nArrPos);
		cout<<nArr[nCurPos]<<" ";
	}
	else
	{
		Print(nArr,nCurLen,nCurPos - 1,nArrPos);
	}
}

void LIS(int nArr[],int nLen)
{
	assert(nLen > 0);
	int nEnd = 0;
	int* nArrIncr = new int[nLen];
	int* nArrPos = new int[nLen];
	nArrIncr[0] = nArr[0];
	nArrPos[0] = 1; //每一个元素所在LIS的位置
	int nLastNumPos = 0;//LIS最后一个元素的下标
	
	for (int i = 1;i < nLen;i++)
	{
		if (nArr[i] > nArrIncr[nEnd])
		{
			nArrIncr[++nEnd] = nArr[i];
			
			nArrPos[i] = nEnd + 1;//寻找LIS
			nLastNumPos = i;//记录LIS最后一个元素的位置
		}
		else
		{
			//寻找第一个大于等于nArr[i]的数
			int nPos = lower_bound(nArrIncr,nArrIncr + nEnd + 1,nArr[i]) - nArrIncr;
			nArrIncr[nPos] = nArr[i];
			
			nArrPos[i] = nPos + 1;//记录每一个数是在LIS中第几个数
		}
	}
	//输出信息
	cout<<"长度: "<<nEnd + 1<<endl;
	cout<<"LIS: ";
	Print(nArr,nEnd + 1,nLastNumPos,nArrPos);
	cout<<endl;
}

int main()
{
	//int nArr[8] = {1,-1,2,-3,4,-5,6,-7};
	//LIS(nArr,8);

 	int nArr[1] = {-7};
 	LIS(nArr,1);

 	//int nArr[8] = {-7,10,9,2,3,8,8,1};
 	//LIS(nArr,8);
	system("pause");
	return 1;
}

抱歉!评论已关闭.