首页 > 代码库 > 数据结构与算法学习之路:LIS——最长递增序列的动态规划算法和二分思想算法

数据结构与算法学习之路:LIS——最长递增序列的动态规划算法和二分思想算法

一、最长递增序列的问题描述:

求一个整数序列的最长递增子序列,子序列不要求是连续的。例如:

Input:4,6,9,6,7,6,3,8,10;Output:5


二、解决方法:

1、用动态规划的方法解决。从问题我们可以知道,我们最终得到的最长递增子序列,其任意一段子序列也是对应序列中的最长子序列。这样说可能不好理解,就以上面的例子来说:

最长子序列为:4,6, 7, 8, 10。在这段子序列的子序列里选一个,例如:4,6,7。则4,6,7也是4,6,9,6,7,6,3这段序列的最长子序列。


对于动态规划思想,我们最重要的是找到状态状态转移方程,那我们来看看这里怎么得到这两个关键东西:


假设,nums={4,6,9,6,7,6,3,8,10},f(x)表示前x个数以nums[x]结尾的序列中,最长子序列的长度,那么:


f(1)=1;f(2)=max{f(1)+1,1}=2;f(3)=max{f(2)+1,1},……以此类推。这里或许有人问了,为什么是f(x-1)+1和1比较啊?原因很简单:以f(2)为例,f(2)=f(1+1),而f(1)=1,也就是说,f(2)的结果是和f(1)关联的,而f(2)本身具有一个初始值1,根据问题要求f(2)的最终值应该是通过f(1)运算所得值和它的初始值选大的那一个。后面的也就以此类推了。


2、这种方法非常巧妙,主要思想是这样的,我不管你的序列是由什么组成的,我只在乎这个序列的长度是多少。于是我创建一个数组d[length],d[0]用来存储最长序列长度,d[i]表示最长递增序列长度为i时,序列的结尾元素。通过二分查找不断的为元素找插入的位置,构成一个最长序列。


这种解法的关键在于:我们要获得的最长递增序列,事实上只要保证每个位置的数字足够小,而且有序,那么得到的自然是最长子序列。说的可能不太清楚,大家可以通过代码感受一下我的意思。


三、代码:


1、O(n^2)的DP算法


int lis1(int* nums, int length){
	int lis_len = 1;
	int d[9];

	for (int i = 0; i < length; ++i){
		d[i] = 1;
		for (int j = 0; j < i; ++j)
			if (nums[i] >= nums[j] && d[i] < d[j] + 1)
				++d[i];
		if (lis_len < d[i])
			lis_len = d[i];
	}

	return lis_len;
}

2、O(nlogn)的二分思想算法

int lis2(int* nums, int length){
	int d[9];
	int mid, left, right;

	d[0] = 1;
	d[1] = nums[0];

	for (int i = 1; i < length; ++i){
		left = 1;
		right = d[0];
		while (left <= right){
			mid = (left + right) / 2;
			if (nums[i] > d[mid])
				left = mid + 1;
			else
				right = mid - 1;
		}
		d[left] = nums[i];
		if (left > d[0])
			++d[0];
	}
	return d[0];
}


数据结构与算法学习之路:LIS——最长递增序列的动态规划算法和二分思想算法