最长子序列

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

思路1:第一眼看到题目,很多人肯定第一时间想到的是LCS。先给数组排个序形成新数组,然后再把新数组和原数组拿来求LCS,即可得到答案。这种解法很多人能想得到,所以就不再赘述。

思路2:按照思路1的想法,最后求LCS时还是得用到DP,我们干嘛不直接用DP来求解呢。对于数组arr,我们从后往前遍历数组,分别求出当子序列以arr[i]结尾时的最长子序列,然后取其中的最大值。即可得到整个数组的最长子序列。 那么怎么求以arr[i]结尾时的最长子序列呢,这就转换成一个DP问题了。要求arr[i]的最长子序列,只需要求出arr[i-1]的最长子序列。即:max{arr[i]}=max{arr[i-1]}+1。

代码:

public static void main(String[] args){

//        int[] arr = {89, 256, 78, 1, 46, 78, 8};
        int[] arr = {1, 3, 5, 2, 4, 6, 7, 8};
//        int[] arr = {6, 4, 8, 2, 17};

        int max = 0;
        int maxLen = arr.length;

        // 从后往前遍历数组,分别求出以arr[i]结尾的时候的最长子序列长度
        for(int i = arr.length-1; i > 0; i--){
            int[] newArr = new int[i];
            System.arraycopy(arr, 0, newArr, 0, i);
            int currentLength = 1+dp(newArr, arr[i]);
            if(currentLength > max)
                max = currentLength;

            // 最长子序列的长度最长为原始数组的长度,
            // 因为不需要我们求最长子序列的元素,所以直接结束循环,减少时间开销
            if(max == maxLen)
                break;
        }

        System.out.println(max);
    }

    public static int dp(int[] arr, int end){
        // 递归结束条件
        if(arr.length == 1){
            // 小于end则包含在子序列中,子序列长度+1
            if(arr[0] <= end)
                return 1;
            else
                return 0;
        }

        // 遍历数组,找到最靠近end的并且<=end的元素位置i
        for(int i=arr.length-1; i>=0; i--){
            if(arr[i] <= end){
                // 从i处截断数组,将arr[0]到arr[i-1]组成新数组继续递归求长度
                int[] newArr = new int[i];
                System.arraycopy(arr, 0, newArr, 0, i);

                // 分别计算包含arr[i]时的最长子序列和不包含arr[i]时的最长子序列,取最大值
                int containLen = dp(newArr, arr[i])+1;
                int notContainLen = dp(newArr, end);
                return containLen > notContainLen ? containLen : notContainLen;
            }
        }

        // 如果没找到比end更小的,返回长度为0
        return 0;

    }

我的方法由于中间开辟了多个新数组,可能占用的空间有点多,不过我觉得应该也不是很多- -,具体我也没统计过。如果有不对的地方还请指正。

呕心沥血写出来的,转载请一定注明出处!

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值