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

1. 前言

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

2. 问题描述

这里写图片描述

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 970655147
 */

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. 运行结果

这里写图片描述

6. 总结

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

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

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
产品简介: 《一彩送货单管理系统》,最好用的一款送货单打印软件,本软件根据市场需要结合企业自身特点量身定做的操作方便、功能完善功能强大个性化极强的送货系统管理系统。 系统集送货单管理,产品管理,客户管理,月结单管理,数据分析,图表分析为一体。送货单号由系统自动生成(可按年+月+日+流水号)单号规则自己定义真正的方便实用。充分利用强大的数据库功能给工作带来的便利,配合强大的权限管理,真正的让您从此告别手写送货单和EXCEL做月结的烦恼,打印出整齐漂亮的送货单。 软件功能 容易上手、通俗易懂; 自行生成对帐单(月结单),自动进行送货统计,自动生成各种统计分析图表; 根据折扣,数量,单价自动计算总价; 送货单可按多种方式进行综合查询; 产品资料管理及查询打印,产品类别管理,客户资料管理及查询打印; 提供多种送货单格式选择; 具有数据备份与数据修复功能; 强大系统自定义功能; 强大的用户管理及用户权限设置; 自动调出客户上次送货记录,产品最后一次价格; 批量打印送货单; 系统所有报表可以轻松的导出Excel , Word ,Jeg图片格式,让报表更加灵活; 软件优势 绿色软件免安装,直接放在U盘中就可以使用; 支持即输即打(不用先建客户、产品资料即可开单); 支持批量打印送货单; 支持批量签回单功能; 支持扫描枪读取单号功能; 单据编号规则自己定义; 系统所有报表都可以轻松的导出Excel Word 以及JPG图片; 自动根据折扣、数量、单价计算出总价; 自动根据规格计算面积; 自动进行数据备份,数据压缩; 本软件操作简单,易上手。配有详细的操作说明,让你快速的完成送货单录入工作, 把机械的工作交给电脑,把时间留给自己。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值