根据
leetcode的第300题:输出最长递增子序列的长度
f[i]表示以i结尾的最长递增子序列的长度
//时间复杂度O(N*N)解法
public int lengthOfLIS(int[] nums) {
if(nums==null||nums.length==0)
return 0;
int n=nums.length;
int[] f=new int[n];
int res=0;
for(int i=0;i<n;i++){
//最长子序列就只有它一个
f[i]=1;
for(int j=0;j<i;j++){
if(nums[j]<nums[i]&&f[j]+1>f[i])
f[i]=f[j]+1;
}
res=Math.max(res,f[i]);
}
return res;
}
下面有两个变形题:
输出所有的最长递增子序列
输出字典序最小的最长递增子序列
这两个题也可以使用动态规划来解,只不过状态数组改为记录以i为结尾的最长子序列
使用HashMap
来记录状态
key是坐标i,value是以i坐标为结尾的最长子序列,因为这个最长子序列可能会有多个,所以采用二维数组的方式
//打印长度最长的所有子序列
public int allLIS(int[] nums) {
if (nums == null || nums.length == 0)
return 0;
int n = nums.length;
//record保存 以下标i为结尾的数字的最长递增子序列的集合,因为可能有多个,所以是二维数组
//将原来的记录长度的dp数组,改为记录子序列的hashMap
HashMap<Integer, List<List<Integer>>> record = new HashMap<>();
//统计最长长度
int max = 0;
for (int i = 0; i < n; i++) {
//i下标对应的二维数组
List<List<Integer>> tmpLists = new ArrayList<>();
//初始化长度为1,即只有它自己
int longest = 1;
//之后枚举从0~i-1,找到还长的i
for (int j = 0; j < i; j++) {
//得到i对应的二维数组,并遍历它的所有一维数组
List<List<Integer>> getLists = record.get(j);
for (int k = 0; k < getLists.size(); k++) {
List<Integer> getList = getLists.get(k);
//如果尾数字比当前的nums[i]小
if (getList.get(getList.size() - 1) < nums[i]) {
//如果长度+1后小于当前长度,结束当前遍历
if(getList.size()+1<longest)
continue;
//如果长度+1大于或等于当前长度,拷贝此序列,并将nums[i]加到最后
List<Integer> tmpList = new ArrayList<>();
for (Integer tmp:getList){
tmpList.add(tmp);
}
tmpList.add(nums[i]);
if (getList.size() + 1 > longest) {
//需要注意的是:如果长度更长,之前的二维数组舍弃,创建一个新的二维数组
tmpLists = new ArrayList<>();
tmpLists.add(tmpList);
longest = tmpList.size();
} else if (getList.size() + 1 == longest) {
tmpLists.add(tmpList);
}
}
}
}
//如果没有满足条件的j,那就只能i自己一个序列
if (tmpLists.size() == 0) {
List<Integer> tmpList = new ArrayList<>();
tmpList.add(nums[i]);
tmpLists.add(tmpList);
}
record.put(i, tmpLists);
if (longest > max)
max = longest;
}
for (Map.Entry<Integer, List<List<Integer>>> entry : record.entrySet()) {
List<List<Integer>> resLists = entry.getValue();
//打印长度为max的所有序列
if (resLists.get(0).size() == max){
for (List<Integer> tmp : resLists) {
System.out.println(tmp);
}
}
}
return max;
}
测试数据:
使用二维数组来记录以i结尾的字典序最小的最长递增子序列,因为只有唯一的一个最小字典序,所以我们使用二维数组来保存序列就可以
public int DictionoryOfLIS(int[] nums){
if (nums == null || nums.length == 0)
return 0;
int n = nums.length;
int max=0;
//因为只记录以i结尾的字典序最小的递增子序列,所以我们只使用二维数组就可以记录序列的状态
List<List<Integer>> record=new ArrayList<>();
//先初始化,方便后面操作
for(int i=0;i<n;i++){
record.add(new ArrayList<>());
}
record.get(0).add(nums[0]);
for (int i = 1; i < n; i++) {
//先假设最长长度为1
int longest=1;
for (int j = 0; j < i; j++) {
//得到以坐标j为结尾的数组
List<Integer> getList=record.get(j);
if(getList.get(getList.size()-1)<nums[i]){
//如果长度比longest还小,直接返回
if(getList.size()+1<longest)
continue;
//如果长度大于等于longest,就先构造出该子序列
List<Integer> tmplist=new ArrayList<>();
for(Integer tmp:getList)
tmplist.add(tmp);
tmplist.add(nums[i]);
//如果大于longest,直接替换
if(getList.size()+1>longest){
// record.add(tmplist);
record.set(i,tmplist);
longest=tmplist.size();
//如果等于,比较字典序,字典序小的保留
}else if(getList.size()+1==longest){
if(!dictoionary(record.get(i),tmplist)){
record.set(i,tmplist);
}
}
}
}
//如果i前面没有符合条件的数,那么序列就是它自己
if(record.get(i).size()==0){
List<Integer> tmplist=new ArrayList<>();
tmplist.add(nums[i]);
record.set(i,tmplist);
}
//记录最长的序列
if(record.get(i).size()>max)
max=record.get(i).size();
}
//返回字典序最小且最长的递增子序列
List<Integer> res=new ArrayList<>();
for (List<Integer> tmp:record) {
if(res.size()<tmp.size()){
res=tmp;
}else if(res.size()==tmp.size()){
if(!dictoionary(res,tmp))
res=tmp;
}
}
System.out.println(res);
return max;
}
private boolean dictoionary(List<Integer> list, List<Integer> getList) {
for (int i = 0; i < getList.size(); i++) {
//如果原来的字典序比较大,就返回false
if(list.get(i)>getList.get(i))
return false;
}
//如果原来的字典序较小,返回true
return true;
}