最长递增子序列

题目:

给定数组arr,返回arr的最长递增子序列。
举例:
arr=[2,1,5,3,6,4,8,9,7],返回的最长递增子序列为[1,3,4,9,8]。

思路:

采取两层循环来遍历数组arr,第一层循环遍历完一个数之后,在第二层循环中以第一层循环中的这个数为第一个数向左进行循环寻找比第一层循环中小的数,并取出这个数的长度并加一。这里需要用到一个判断公式:

dp[i] = max(dp[i],dp[j]+1);

数组dp存储的是数组arr中每一个数作为结尾的最长递增子序列的长度。之后依靠着dp数组寻找最长递增子序列。

解题代码:

/**
 * 最长递增子序列的求解方式
 * @author Administrator
 *
 */
public class problem1 {
    /**
     * 用于返回最长递增子序列
     * @param arr 求解的目标数组
     * @return
     */
    public ArrayList<Integer> theLongest(int [] arr){
        ArrayList<Integer>list = new ArrayList<Integer>();

        int [] dp = this.dpValue(arr);
        int max = 0,index = 0;
        for(int i=0;i<dp.length;i++){
            if(dp[i]>max){
                max = dp[i];
                index = i;
            }
        }

        list.add(arr[index]);
        max--;
        //查找最长递增子序列
        for(int i=index-1;i>=0;i--){
            if(dp[i] == max){
                list.add(arr[i]);
                max--;
            }
        }
        //翻转list数组得到结果
        Collections.reverse(list);
        return list;
    }

    /**
     * 用于计算数组中以每个数字结尾的最长长度
     * @param arr 目标数组
     * @return
     */
    public int [] dpValue(int [] arr){
        //定义数组用于储存以每个数作为结尾的最长长度
        int [] dp = new int [arr.length];

        for(int i=0;i<dp.length;i++){
            dp[i] = 1;
            for(int j=i;j>=0;j--){
                if(arr[i]>arr[j]){
                    dp[i] = Math.max(dp[i], dp[j]+1);
                }
            }
        }

        return dp;
    }

}

这里有一个问题,此种解法最终得到的时间复杂度是O(N^2),有没有什么办法可以将其的时间复杂度降下来,二分查找是一个好的解决办法可以将它的时间复杂度降到O(NlogN).但是这里需要重新开一个数组,也就是牺牲空间来换取时间上的优势。以下是此种思路的代码:

   /**
     * 用于计算数组中以每个数字结尾的最长长度
     * @param arr 目标数组
     * @return
     */
    public int [] dpValue(int [] arr){
        //定义数组用于储存以每个数作为结尾的最长长度
        int [] dp = new int [arr.length];
        int [] ends = new int [arr.length];
        int right = 0;
        int r = 0;

        dp[0] = 1;
        ends[0] = arr[0];
        for(int i=1;i<dp.length;i++){
            r = right;
            int index = this.location(ends, 0, r, arr[i]);
            right = Math.max(right, index);
            ends[index] = arr[i];
            dp[i] = index+1;
        }

        return dp;
    }

    /**
     * 二分查找法
     * @param arr
     * @param left
     * @param right
     * @param num
     * @return
     */
    public int location(int [] arr,int left,int right,int num){
        int l = left;
        int r = right;
        int mid = 0;
        while(l<=r){
            mid = (l+r)/2;
            if(arr[mid] == num){
                return mid;
            }
            if(arr[mid]>num){
                r = mid-1;
            }else{
                l = mid+1;
            }
        }
        return l;
    }
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值