【动态规划】输出所有的最长递增子序列和字典序最小的

leetcode 300

根据
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<Integer, List<List<Integer>>> record来记录状态
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;  
    }

在这里插入图片描述

  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值