之前学习了动态规划中最基本的问题,最长公共子序列,具体解法,见前前一篇博客:

http://www.cnblogs.com/liyukuneed/archive/2013/05/22/3090597.html

本篇博客要继续解决一个升级的问题——最长递增子序列

问题定义:

给定一个长度为N的数组,找出一个最长的单调自增子序列(不一定连续,但是顺序不能乱)。例如:给定一个长度为6的数组A{5, 6, 7, 1, 2, 8},则其最长的单调递增子序列为{5,6,7,8},长度为4.

解法一:最长公共子序列法:

仔细思考上面的问题,其实可以把上面的问题转化为求最长公共子序列的问题。原数组为A{5, 6, 7, 1, 2, 8},下一步,我们对这个数组进行排序,排序后的数组为A‘{1, 2, 5, 6, 7, 8}。我们有了这样的两个数组后,如果想求数组A的最长递增子序列,其实就是求数组A与它的排序数组A‘的最长公共子序列。我来思考下原问题的几个要素:最长、递增、子序列(即顺序不变)。

递增:A‘数组为排序数组,本身就是递增的,保证了两序列的最长公共子序列的递增特性。

子序列:由于A数组就是原数组,其任意的子序列都是顺序不变的,这样就保证了两序列的最长公共子序列的顺序不变。

最长:显而易见。

具体的解法请参见上一篇博客:

http://www.cnblogs.com/liyukuneed/archive/2013/05/22/3090597.html

解法二:动态规划法(O(N^2))

既然是动态规划法,那么最重要的自然就是寻找子问题,对于这个问题,我们找到他的子问题:

对于长度为N的数组A[N] = {a0, a1, a2, ..., an-1},假设假设我们想求以aj结尾的最大递增子序列长度,设为L[j],那么L[j] = max(L[i]) + 1, where i < j && a[i] < a[j], 也就是i的范围是0到j - 1。这样,想求aj结尾的最大递增子序列的长度,我们就需要遍历j之前的所有位置i(0到j-1),找出a[i] < a[j],计算这些i中,能产生最大L[i]的i,之后就可以求出L[j]。之后我对每一个A[N]中的元素都计算以他们各自结尾的最大递增子序列的长度,这些长度的最大值,就是我们要求的问题——数组A的最大递增子序列。

时间复杂度:由于每一次都要与之前的所有i做比较,这样时间复杂度为O(N^2)。

解法三:动态规划法(O(NlogN))

上面的解法时间复杂度仍然为O(N^2),与解法一没有明显的差别。仔细分析一下原因,之所以慢,是因为对于每一个新的位置j都需要遍历j之前的所以位置,找出之前位置最长递增子序列长度。那么我们是不是可以有一中方法能不用遍历之前所有的位置,而可以更快的确定i的位置呢?

这就需要申请一个长度为N的空间,B[N],用变量len记录现在的最长递增子序列的长度。

B数组内任意元素B[i],记录的是最长递增子序列长度为i的序列的末尾元素的值,也就是这个最长递增子序列的最大元素的大小值。

首先,把d[1]有序地放到B里,令B[1] = 2,就是说当只有1一个数字2的时候,长度为1的LIS的最小末尾是2。这时Len=1

然后,把d[2]有序地放到B里,令B[1] = 1,就是说长度为1的LIS的最小末尾是1,d[1]=2已经没用了,很容易理解吧。这时Len=1

接着,d[3] = 5,d[3]>B[1],所以令B[1+1]=B[2]=d[3]=5,就是说长度为2的LIS的最小末尾是5,很容易理解吧。这时候B[1..2] = 1, 5,Len=2

再来,d[4] = 3,它正好加在1,5之间,放在1的位置显然不合适,因为1小于3,长度为1的LIS最小末尾应该是1,这样很容易推知,长度为2的LIS最小末尾是3,于是可以把5淘汰掉,这时候B[1..2] = 1, 3,Len = 2

继续,d[5] = 6,它在3后面,因为B[2] = 3, 而6在3后面,于是很容易可以推知B[3] = 6, 这时B[1..3] = 1, 3, 6,还是很容易理解吧? Len = 3 了噢。

第6个, d[6] = 4,你看它在3和6之间,于是我们就可以把6替换掉,得到B[3] = 4。B[1..3] = 1, 3, 4, Len继续等于3

第7个, d[7] = 8,它很大,比4大,嗯。于是B[4] = 8。Len变成4了

第8个, d[8] = 9,得到B[5] = 9,嗯。Len继续增大,到5了。

最后一个, d[9] = 7,它在B[3] = 4和B[4] = 8之间,所以我们知道,最新的B[4] =7,B[1..5] = 1, 3, 4, 7, 9,Len = 5。

于是我们知道了LIS的长度为5。

注意,这个1,3,4,7,9不是LIS,它只是存储的对应长度LIS的最小末尾。有了这个末尾,我们就可以一个一个地插入数据。虽然最后一个d[9] = 7更新进去对于这组数据没有什么意义,但是如果后面再出现两个数字 8 和 9,那么就可以把8更新到d[5], 9更新到d[6],得出LIS的长度为6。

然后应该发现一件事情了:在B中插入数据是有序的,而且是进行替换而不需要挪动——也就是说,我们可以使用二分查找,将每一个数字的插入时间优化到O(logN)~~~~~于是算法的时间复杂度就降低到了O(NlogN)~!

根据上面的分析,下面是后两种方法的C++代码实现:

#include <iostream>using namespace std;// LIS[j] = max(LIS[i]) + 1
int LIS_DP_N2(int *array, int nLength)
{int LIS[nLength];for(int i = 0; i < nLength; i++){LIS[i] = 1;}for(int i = 1; i < nLength; i++){int maxLen = 0;for(int j = 0; j < i; j++){if(array[i] > array[j]){if(maxLen < LIS[j])maxLen = LIS[j];}}LIS[i] = maxLen + 1;}int maxLIS = 0;for(int i = 0; i < nLength; i++){if(maxLIS < LIS[i])maxLIS = LIS[i];}return maxLIS;
}int BinarySearch(int *array, int value, int nLength)
{int begin = 0;int end = nLength - 1;while(begin <= end){int mid = begin + (end - begin) / 2;if(array[mid] == value)return mid;else if(array[mid] > value)end = mid - 1;elsebegin = mid + 1;}return begin;
}int LIS_DP_NlogN(int *array, int nLength)
{int B[nLength];int nLISLen = 1;B[0] = array[0];for(int i = 1; i < nLength; i++){if(array[i] > B[nLISLen - 1]){B[nLISLen] = array[i];nLISLen++;}else{int pos = BinarySearch(B, array[i], nLISLen);B[pos] = array[i];}}return nLISLen;
}int main()
{int data[6] = {5, 6, 7, 1, 2, 8};cout<<LIS_DP_N2(data, 6)<<endl;cout<<LIS_DP_NlogN(data, 6)<<endl;return 0;
}

View Code

转载于:https://www.cnblogs.com/For-her/p/3785682.html

跟着编程之美学算法——最长递增子序列(转)相关推荐

  1. 动态规划算法 | 最长递增子序列

    通过查阅相关资料发现动态规划问题一般就是求解最值问题.这种方法在解决一些问题时应用比较多,比如求最长递增子序列等. 有部分人认为动态规划的核心就是:穷举.因为要求最值,肯定要把所有可行的答案穷举出来, ...

  2. 动态规划算法04-最长递增子序列问题

    最长递增子序列问题 简述 经典的动态规划问题. 问题描述 给定一个序列,求解其中长度最长的递增子序列. 问题分析 这种可以向下查询答案的很容易想到动态规划的解法. 要求长度为i的序列Ai={a1,a2 ...

  3. 数组字符串那些经典算法:最大子序列和,最长递增子序列,最长公共子串,最长公共子序列,字符串编辑距离,最长不重复子串,最长回文子串 (转)...

    作者:寒小阳 时间:2013年9月. 出处:http://blog.csdn.net/han_xiaoyang/article/details/11969497. 声明:版权所有,转载请注明出处,谢谢 ...

  4. JavaScript实现LongestIncreasingSubsequence最长递增子序列算法(附完整源码)

    JavaScript实现LongestIncreasingSubsequence最长递增子序列算法(附完整源码) dpLongestIncreasingSubsequence.js完整源代码 dpLo ...

  5. 动态规划算法-04最长递增子序列问题

    最长递增子序列问题 简述 经典的动态规划问题. 问题描述 给定一个序列,求解其中长度最长的递增子序列. 问题分析 这种可以向下查询答案的很容易想到动态规划的解法. 要求长度为i的序列Ai={a1,a2 ...

  6. 编程之美 求数组中的最长递增子序列

    如题,例如:存在数组 1,-1,2,-3,4,-5,6,-7 ,则最长的递增子序列是:1,2,4,6. 法一: 蛮力法 int Lis(int* arr,int n) {int iCount=0;// ...

  7. 最长递增子序列_python_算法与数据结构

    周末了,实验室的网速还是不给力啊,不知道doctors都在干啥,,,最近都在做算法作业,昨天晚上看了一部电影<将爱进行到底>,刚打开电影没多久就听到了很熟悉的旋律,让我很是惊讶,这竟然就是 ...

  8. 计算机算法设计与分析 最长递增子序列

    求一个字符串的最长递增子序列的长度.设计基于动态规划思想的算法. 如:dabdbf最长递增子序列就是abdf,长度为4 输入第一行一个整数0<n<20,表示有n个字符串要处理,随后的n行, ...

  9. 算法设计 - LCS 最长公共子序列最长公共子串 LIS 最长递增子序列

    出处 http://segmentfault.com/blog/exploring/ 本章讲解: 1. LCS(最长公共子序列)O(n^2)的时间复杂度,O(n^2)的空间复杂度: 2. 与之类似但不 ...

最新文章

  1. CentOS7安装Python3详细步骤与Python2共用方法
  2. 小学生正确使用计算机,小学生做数学作业用计算器的做法正确吗?为什么?
  3. python 100题
  4. python如何在官网下载1005无标题,如何安装python cairo?
  5. 基于行为树的新手引导设计
  6. intx update task - IB_IBINTX_UPDATE
  7. 揭秘!2020年4月全国程序员工资统计,新出炉!(包含地区和语言排行)
  8. 三大类sql语句——该记录是本人以前微博上的文章
  9. mysql执行的list_CMD如何进入Mysql命令并执行相关查询
  10. 斗地主AI算法——第四章の权值定义
  11. 每日一题(C语言基础篇)2
  12. 【博客美化】02.公告栏显示个性化时间
  13. windows权限了解
  14. 百度文库如何免费下载
  15. 解决Steam绑定手机令牌的问题
  16. OpenSSL源码分析—MD4算法实现
  17. html文字左侧居中,HTML如何让文字靠左居中?
  18. 超过2T硬盘用不了,怎么办?
  19. RRPP和smart link 综合实验
  20. 宝塔免费ssl证书是什么

热门文章

  1. 编程语言对比 基本数据类型
  2. D3 插入删除元素元素
  3. 软件测试测试用例编写 不超过7步骤_教你快速编写一个合格的测试用例!
  4. Java基础学习总结(185)—— Java 在云原生时代的进化
  5. 分库分表学习总结(5)——有关分库分表相关面试题总结
  6. linux目录结构来源6,Linux入门基础 #6 Linux系统目录架构
  7. php rest api lumen,lumen Rest API 起步
  8. 【直播回顾】云栖社区特邀专家关键:Java无锁集合代码分析
  9. C#面向对象15 多态
  10. 1191 消灭兔子(贪心+优先队列)