1. 前言

本文的一些图片, 资料 截取自编程之美

2. 问题描述

2.16 求一个数列的最长递增子序列_递增子序列

3. 问题分析

对于这种求优问题, 最基本的思路, 无疑便是穷举了

解法一 : 使用一个incSeq数组存放包含第i个数之前的最长的递增子序列的长度, 将每一个位置的最长递增子序列长度初始化为1, 然后遍历一次数组[循环变量为n], 在遍历的过程中再一次遍历incSeq (0, curN)[循环变量为i], 如果当前数大于arr[i], 并且incSeq[i]+1 > incSeq[n], 则更新incSeq[n]

解法二 : 使用一个incSeq数组存放包含第i个数之前的最长的递增子序列的长度, 将每一个位置的最长递增子序列长度初始化为1, 使用一个maxValue数组存放”递增序列长度为i的子序列中最大值”中的最小的一个, 遍历一次数组[循环变量为i], 找出arr[i]能够满足的最大的maxValue[curMaxLen]
然后判断curMaxLen, 是否大于maxLen,
    如果大于 则更新maxLen, 更新maxValue[curMaxLen],
    否则 则判断arr[i] 是否小于maxValue[curMaxLen],
        如果是 则更新maxValue[curMaxLen]

解法三 : 根据max[i] 的单调递增关系, 初始化上面的找出arr[i]能够满足的最大的maxValue的上界为incSeq[i-1], 但是, 根据上面解法二的代码分析, max[i], 应该不是单调递增的呀,
假设是incSeq现在将其定义改变为第i个数之前的最长的递增子序列的长度, 那么将上界定为[incSeq[i-1] ]的意义貌似也大呀, 应为incSeq[i-1] = maxLen
算了 先将这个问题放在这里, 希望有网友能够帮我解答一下这个解法的思路

4. 代码

/**
 * file name : Test23FindMaxIncSeq.java
 * created at : 4:58:07 PM May 24, 2015
 * created by 
 */

package com.hx.test03;

public class Test23FindMaxIncSeq {

    // 找到最大的递增序列
    public static void main(String []args) {

        int[] intArr = {-50, 0, -14, -15, 32, -34, 19, 32, -25, -50, 41, -6, 45, 26, -38, 36, -4, -13, -8, -36 };

        findMaxIncSeq01(intArr);
        findMaxIncSeq02(intArr);
        findMaxIncSeq03(intArr);

    }

    // 穷举   incSeq[i]表示包含第i个元素  之前的递增的元素的最大长度
    public static void findMaxIncSeq01(int[] intArr) {
        int[] incSeq = new int[intArr.length];

        for(int i=0; i<intArr.length; i++) {
            incSeq[i] = 1;

            // 遍历前面的元素  
            // 如果该元素小于intArr[i], 并且该元素的递增最大长度+1 大于intArr[i]的当前递增长度    则更新intArr[i]的递增长度
            for(int j=0; j<i; j++) {
                if(intArr[i] > intArr[j] && incSeq[j] + 1 > incSeq[i]) {
                    incSeq[i] = incSeq[j] + 1;
                }
            }
        }

        int max = Tools.getMaxInArr(incSeq);
        Log.log(max);
    }

    // incSeq[i]表示包含第i个元素  之前的递增的元素的最大长度
    // maxV[i]表示长度为i 的递增子序列[可能存在多个长度为i的序列]的最大元素 的最小值
    // 遍历intArr  找出intArr[i]满足的最长子序列, 更新incSeq[i] [之所以找出intArr[i] 满足的最长的子序列[maxSeqIdx], 是因为, 如果后面存在一个数字其大于[maxSeqIdx], 那么其一定会大于[maxSeqIdx-1], 二者同步增长, [maxSeqIdx-1]的序列永远不可能超过[maxSeqIdx]对应的序列, 所以不用更新[maxSeqIdx]之前的序列]
        // 如果incSeq[i]大于maxLen   更新maxLen, maxV[j+1]
        // 否则 如果存在intArr[i]满足的序列  并且maxV[j+1]大于intArr[i]  更新maxV[j+1]为intArr[i]
    public static void findMaxIncSeq02(int[] intArr) {
        int[] maxV = new int[intArr.length + 1];
        maxV[1] = intArr[0];
        maxV[0] = Tools.getMinInArr(intArr) - 1;
        int[] incSeq = new int[intArr.length];
        int maxLen = 1;

        // 遍历intArr
        for(int i=1; i<intArr.length; i++) {
            incSeq[i] = 1;

            int j;
            // 找到intArr[i]能满足的最长子序列   更新incSeq[i], 之后的代码中j表示 该子序列的长度
            for(j = maxLen; j>=0; j--) {
                if(intArr[i] > maxV[j]) {
                    incSeq[i] = j + 1;
                    break;
                }
            }

            // 如果第i个元素的递增子序列 超过了maxLen   则更新maxLen, 更新maxV[最大序列的长度]为intArr[i]
            // 否则  如果intArr[i] 大于maxV[j][说明更新了incSeq[i]], 并且小于maxV[j+1]    更新maxV[j+1] = intArr[i]
            if(incSeq[i] > maxLen) {
                maxLen = incSeq[i];
                maxV[incSeq[i]] = intArr[i];
            } else if(maxV[j] < intArr[i] && intArr[i] < maxV[j+1]) {
                maxV[j+1] = intArr[i];
            }
        }

        Log.log(maxLen);
    }

    // incSeq[i]表示包含第i个元素  之前的递增的元素的最大长度
    // maxV[i]表示长度为i 的递增子序列[可能存在多个长度为i的序列]的最大元素 的最小值
    public static void findMaxIncSeq03(int[] intArr) {

        int[] maxV = new int[intArr.length + 1];
        maxV[0] = Tools.getMinInArr(intArr) - 1;
        maxV[1] = intArr[0];
        int[] incSeq = new int[intArr.length];

        for(int i=0; i<incSeq.length; i++) {
            incSeq[i] = 1;
        }
        int maxLen = 1;

        // 遍历intArr
        for(int i=1; i<intArr.length; i++) {
            int j;
            // 这个算法 相对于上面的算法  仅仅是在j的初始化上做了一个修改 以及结束条件
            // ... 但是 修改之后   好像还错了
            // 找到intArr[i]能满足的最长子序列   更新incSeq[i], 之后的代码中j表示 该子序列的长度
            for(j = incSeq[i-1]; j>=1; j--) {
                if(intArr[i] > maxV[j]) {
                    incSeq[i] = j + 1;
                    break;
                }
            }

            // 如果第i个元素的递增子序列 超过了maxLen   则更新maxLen, 更新maxV[最大序列的长度]为intArr[i]
            // 否则  如果intArr[i] 大于maxV[j][说明更新了incSeq[i]], 并且小于maxV[j+1]    更新maxV[j+1] = intArr[i]
            if(incSeq[i] > maxLen) {
                maxLen = incSeq[i];
                maxV[incSeq[i]] = intArr[i];
            } else if(maxV[j] < intArr[i] && intArr[i] < maxV[j+1]) {
                maxV[j+1] = intArr[i];
            }
        }

        Log.log(maxLen);
    }

}

5. 运行结果

2.16 求一个数列的最长递增子序列_编程之美_02

6. 总结

这个题目是挺难的, 第二种解法知识比第一种解法优化了一些, 但是时间复杂度仍然为O(n^2)

注 : 因为作者的水平有限,必然可能出现一些bug, 所以请大家指出!