最长递增子序列(LIS)
来源:互联网 发布:淘宝卖家双11报名入口 编辑:程序博客网 时间:2024/05/17 09:35
上个月去大众点评面试,面试官让我写LIS,尼玛我当时竟然听成了求“子数组之和的最大值”,写好后被面试官指了出来,我又想当然以为是”子数组“,再次写好后,面试官看了看,也没说什么,我还以为有戏。结果第二天在去趋势面试的路上,收到了拒信,秋风那个吹啊……
直到昨晚看了《编程之美》的2.16,才搞明白”子数组“和”子序列“的区别,晚上躺在床上用我的弱鸡鸭看到了这篇文章http://www.ahathinking.com/archives/117.html,基本明白了原理,今天稍微整理下以备后用。
三种方法:
1. 动态规划
以array[i]为最大元素的最长递增子序列的长度记为LIS[i],显然LIS[i] >= 1,array[i]元素本身至少可提供长度为1的递增子序列。对于array[i],从左到右依次考虑array[i]之前的元素,array[k] (0<=k<i),若array[k]<array[i],且LIS[k]+1 > LIS[i],则用LIS[k]+1来更新LIS[i]。最终可得到最长递增子序列的长度为lisLength。如数组”1 -1 2 -3 4 -5 6 -7“对应的LIS信息为:
数组: 1 -1 2 -3 4 -5 6 -7
LIS记录:1 1 2 1 3 1 4 1
这种方法有一个好处是可以打印出最长递增子序列,从i=array.size()-1开始向前遍历,若LIS[i] == lisLength,则array[i]入栈(或者递归),同时--i,--lisLength;否则--i(向前遍历);当lisLength == 0时终止,打印出最长递增子序列。
具体代码如下:
#include <iostream>#include <vector>#include <sstream>using namespace std;int GetLIS(const vector<int> &data, vector<int> &record){ vector<int>::size_type currPos=1; int maxLength = 1; for (; currPos<data.size(); ++currPos) { for (vector<int>::size_type prevPos=0; prevPos<currPos; ++prevPos) { if (data[prevPos] < data[currPos]) { if (record[prevPos]+1 > record[currPos]) { record[currPos] = record[prevPos] + 1; } } } if (record[currPos] > maxLength) { maxLength = record[currPos]; } } return maxLength;}void PrintLIS(const vector<int> &data, const vector<int> &record, int currPos, int lisLength){ if (lisLength == 0) { return; } while (record[currPos] != lisLength) { --currPos; } PrintLIS(data, record, currPos-1, lisLength-1); cout << data[currPos] << ' ';}void GetLIS(const vector<int> &data){ if (data.empty()) { return; } vector<int> record(data.size(), 1); int lisLength = GetLIS(data, record); cout << lisLength << endl; PrintLIS(data, record, data.size()-1, lisLength); cout << endl;}int main(){ istringstream input("1 -1 2 -3 4 -5 6 -7"); vector<int> data; int iTmp; while (input >> iTmp) { data.push_back(iTmp); } GetLIS(data); return 0;}
这个我没有亲自实践,看参考文章说是将原数组利用QuickSort生成一个排好序的副本,然后求原数组与对应的有序数组的最长公共子序列(LCS)即可。
3. 动态规划+二分查找(吊炸天的方法)
用minLast[i]表示长度为(i+1)的递增子序列(可能有多个)的最大元素(最后一个元素)的最小值,如数组”1 -1 2 -3 4 -5 6 -7“对应的minLast为:minLast[0] = -7,minLast[1] = 2,minLast[2] = 4,minLast[3] = 6,此时不再需要LIS数组。
需要注意的是,对于i < j,必有minLast[i] < minLast[j],否则必可以从小于minLast[j]的元素中找到一个数来取代minLast[i]。根据这个关系便可以执行二分查找,找到对应的更新位置。对于当前元素array[currPos],如果其值大于minLast.back(),则需要更新lisLength,即push_back(array[currPos]);否则利用二分查找找到刚好大于array[currPos]的minLast[updatePos],然后用array[currPos]的值来更新minLast[updatePos],因为在array[currPos]之前的元素中可以找到(updatePos-1)个元素与array[currPos]构成长度为updatePos的递增子序列,且以array[currPos]为最大(最后一个)元素。(对于array[currPos] == minLast[updatePos]的情形,其实并不会造成任何实质上的minLast更新)
具体代码如下:
#include <iostream>#include <sstream>#include <vector>using namespace std;int BinarySearch(const vector<int> &minLast, int value){ int left = 0; int right = minLast.size() - 1; while (left <= right) { int mid = (left+right) / 2; if (minLast[mid] == value) { return mid; } else if (minLast[mid] < value) { left = mid + 1; } else { right = mid - 1; } } return left;}int GetLIS(const vector<int> &data){ int lisLength = 0; vector<int> minLast; vector<int>::size_type pos = 0; for (; pos<data.size(); ++pos) { if (lisLength==0 || data[pos]>minLast.back()) { minLast.push_back(data[pos]); ++lisLength; } else { int updatePos = BinarySearch(minLast, data[pos]); minLast[updatePos] = data[pos]; } }/* for (int i=0; i<minLast.size(); ++i) { cout << minLast[i] << ' '; } cout << endl;*/ return lisLength;}int main(){ istringstream input("1 -1 2 -3 4 -5 6 -7"); vector<int> data; int iTmp; while (input >> iTmp) { data.push_back(iTmp); } cout << GetLIS(data) << endl; return 0;}
- 最长递增子序列 LIS
- 最长递增子序列(LIS)
- 最长递增子序列(LIS)
- 最长递增子序列LIS
- 最长递增子序列LIS
- 最长递增子序列(LIS)
- 最长递增子序列LIS
- 最长递增子序列 LIS
- LIS(最长递增子序列)
- LIS-最长递增子序列
- 最长递增子序列LIS
- 最长递增子序列LIS
- 最长递增子序列问题((LIS))
- 最长递增子序列(LIS)
- 最长递增子序列(LIS)
- LIS最长递增子序列DPC++实现
- 最长递增子序列(LIS)
- 最长递增子序列LIS算法
- 读写锁的实现
- hibernate之HQL之子查询
- 查看android目录结构
- Spring.net 在mvc中的用法
- 初探js特效魅力之延时提示框07
- 最长递增子序列(LIS)
- 权限管理系统--Bootstrap框架/JasigCAS单点登录/Dubbo接口授权
- git使用ssh密钥
- poj 1556(floyd)
- 树的层次遍历
- eclipse配置Android sdk 时,网址:http://dl-ssl.google.com/android/ 无法连接解决办法
- 获取IP地址
- 基于Tomcat和Oracle的连接池技术的Jdbc连接
- 平衡树(AVL)详解