zoukankan      html  css  js  c++  java
  • 最长递增子序列问题

    题目描述

    给定一个未排序的整数数组,找出最长递增子序列。
    例如给定数组[10, 9, 2, 5, 3, 7, 101, 18],最长递增子序列就是[2, 3, 7, 101],长度就是4,最长递增子序列不一定只有一个,只要求出最长的长度。


    解法一

    动态规划法,定义一个数组dp,dp[i]代表了第i个数为结尾的最长递增子序列长度。当计算第dp[i]时,比较i位置的值和前面的所有值相比,如果值大于前面j处的值,就记录当前最大的dp[i]为dp[j]+1,dp[i]中的最大值。遍历过程中可以设置一个值记录最大递增子序列。
    时间复杂度:由于每次判断dp[i],需要和i前面的所有值比较,因此,时间复杂度为O(n2)。
    实现:

    1. publicstaticint longestSubstring(int[] arr){
    2. if(arr ==null|| arr.length <=0)return0;
    3. int[] dp =newint[arr.length];
    4. dp[0]=1;
    5. int max = dp[0];
    6. for(int i =1; i < arr.length; i++){
    7. dp[i]=1;
    8. for(int j =0; j < i; j++){
    9. if(arr[j]< arr[i]){
    10. dp[i]= dp[i]>= dp[j]+1? dp[i]: dp[j]+1;
    11. }
    12. }
    13. max = max >= dp[i]? max : dp[i];
    14. }
    15. return max;
    16. }

    解法二

    解法二中,同样定义一个数组h[],h[i]表示的是长度为i+1的递增子序列的最小末尾。h数组是有序的,每次遍历一个数,就在数组h的有效区中(填充了数据的部分)找到第一个大于自己的数,将其覆盖。
    时间复杂度:O(nlogn)
    实现:

    1. publicstaticint longestSubstringFast(int[] arr){
    2. if(arr ==null|| arr.length <=0)return0;
    3. int[] h =newint[arr.length];
    4. h[0]= arr[0];
    5. int low =0;
    6. int high =0;
    7. int curr =0;//记录h的最后一个位置
    8. for(int i =1; i < arr.length; i++){
    9. if(arr[i]> h[curr]){
    10. h[++curr]= arr[i];//比当前最后一个大,则直接插入在末尾
    11. }
    12. else{
    13. low =0;
    14. high = curr;
    15. while(low <= high){
    16. if(h[high]< arr[i]){
    17. h[high+1]= arr[i];
    18. break;
    19. }
    20. if(h[low]> arr[i]){
    21. h[low]= arr[i];
    22. break;
    23. }
    24. int mid =(low + high)/2;
    25. if(arr[i]== h[mid])break;
    26. elseif(arr[i]> h[mid]) low = mid +1;
    27. else high = mid;
    28. }
    29. }
    30. }
    31. return curr+1;
    32. }

    扩展题目

    描述:给定一个N×2 的二维数组,看作是一个个二元组,例如[[a1,b1],[a2,b2],[a3,b3]]
    规定:一个如果想把二元组甲放在二元组乙上,甲中的a 值必须大于乙中的a 值,甲中的b
    值必须大于乙中的b 值。如果在二维数组中随意选择二元组,请问二元组最多可以往上摞
    几个?
    例如:[[5,4],[6,4],[6,7],[2,3]], 最大数量可以摞3 个,[2,3] => [5,4] => [6,7]
    要求:实现时间复杂度O(NlogN)的解法


    解法一

    与上面求最长递增子序列方法一样,首先需要将数组排序,定义一个类用于表示二元组。排序规则为首先对二元组第一个数递增排序,然后第一个数相同的情况下,对第二个数递增排序。其中dp[i]表示以第i个二元组为结尾的最大长度。
    时间复杂度:O(n2)
    实现:
    二元组定义

    1. /**
    2. * 二元组类,保存两个数
    3. * Created by GGM on 2016/7/25.
    4. */
    5. publicclassTwoTuples{
    6. privateint a;
    7. privateint b;
    8. publicint getA(){
    9. return a;
    10. }
    11. publicint getB(){
    12. return b;
    13. }
    14. publicTwoTuples(int a,int b){
    15. this.a = a;
    16. this.b = b;
    17. }
    18. publicboolean canBeUpper(TwoTuples twoTuples){
    19. if(this.a > twoTuples.a &&this.b > twoTuples.b)returntrue;
    20. returnfalse;
    21. }
    22. }

    比较器定义

    1. /**
    2. * Created by GGM on 2016/7/25.
    3. */
    4. publicclassComparatorGenerator{
    5. /**
    6. * 第一个数递增排序,第二个数递增排序
    7. * @return
    8. */
    9. publicstaticComparator<TwoTuples> orderComparator(){
    10. returnnewComparator<TwoTuples>(){
    11. @Override
    12. publicint compare(TwoTuples o1,TwoTuples o2){
    13. if(o1.getA()== o2.getA()){
    14. return o1.getB()> o2.getB()?1:(o1.getB()== o2.getB()?0:-1);
    15. }else{
    16. return o1.getA()> o2.getA()?1:-1;
    17. }
    18. }
    19. };
    20. }
    21. /**
    22. * 第一个数大递增排序,第二数递减排序
    23. * @return
    24. */
    25. publicstaticComparator<TwoTuples> reverseComparator(){
    26. returnnewComparator<TwoTuples>(){
    27. @Override
    28. publicint compare(TwoTuples o1,TwoTuples o2){
    29. if(o1.getA()== o2.getA()){
    30. return o1.getB()> o2.getB()?-1:(o1.getB()== o2.getB()?0:1);
    31. }else{
    32. return o1.getA()> o2.getA()?1:-1;
    33. }
    34. }
    35. };
    36. }
    37. }

    算法实现

    1. publicstaticint longestOfTwoTuples(TwoTuples[] arr){
    2. if(arr ==null|| arr.length <=0)return0;
    3. int[] dp =newint[arr.length];
    4. Arrays.sort(arr,ComparatorGenerator.orderComparator());
    5. dp[0]=1;
    6. int max = dp[0];
    7. for(int i =1; i < arr.length; i++){
    8. dp[i]=1;
    9. for(int j =0; j < i; j++){
    10. if(arr[i].canBeUpper(arr[j])){//如果当前i可以放在j上面
    11. dp[i]= dp[i]>= dp[j]+1? dp[i]: dp[j]+1;
    12. }
    13. }
    14. max = max >= dp[i]? max : dp[i];
    15. }
    16. return max;
    17. }

    解法二

    本解法同样需要对数组先排序,排序使用如下规则:对第一个数升序排序,如果第一个数相同,则第二个数降序排序。 h[i]存放的是二元组的b的值,因为相同的a的情况的下,b是按降序排序,相同的a情况下,b会覆盖第一个比它大的值,如果b的值比有效区最后一个值,那么说明是a也比有效区最后一个值,那么,将其添加在后面。
    时间复杂度:O(nlogn)
    实现:
    二元组和比较器在上面定义,此处就省略了
    算法实现

    1. publicstaticint longestOfTwoTuplesFast(TwoTuples[] arr){
    2. if(arr ==null|| arr.length <=0)return0;
    3. int[] h =newint[arr.length];
    4. Arrays.sort(arr,ComparatorGenerator.reverseComparator());
    5. h[0]= arr[0].getB();
    6. int low =0;
    7. int high =0;
    8. int curr =0;//记录h的最后一个位置
    9. for(int i =1; i < arr.length; i++){
    10. if(arr[i].getB()> h[curr]){
    11. h[++curr]= arr[i].getB();//比当前最后一个大,则直接插入在末尾
    12. }
    13. else{
    14. low =0;
    15. high = curr;
    16. while(low <= high){
    17. if(h[high]< arr[i].getB()){
    18. h[high+1]= arr[i].getB();
    19. break;
    20. }
    21. if(h[low]> arr[i].getB()){
    22. h[low]= arr[i].getB();
    23. break;
    24. }
    25. int mid =(low + high)/2;
    26. if(arr[i].getB()== h[mid])break;
    27. elseif(arr[i].getB()> h[mid]) low = mid +1;
    28. else high = mid;
    29. }
    30. }
    31. }
    32. return curr+1;
    33. }





  • 相关阅读:
    android头像更换(实现拍照和从手机图片里选择两种形式)
    安卓开发实战-记账本APP(六)
    安卓开发实战-记账本APP(五)
    安卓开发实战-记账本APP(四)
    安卓开发实战-记账本APP(三)
    BaseAdapter的三种表达式分析,startActivityForResult的使用
    使用Bundle在Activity之间交换数据
    深入理解JVM-类加载器深入解析(3)
    深入理解java内存模型--读书笔记
    深入理解JVM-类加载器深入解析(2)
  • 原文地址:https://www.cnblogs.com/ggmfengyangdi/p/5703219.html
Copyright © 2011-2022 走看看