子序列相关

Posted by 令德湖周杰伦 on 04-30,2024

1. 概念

子序列 是由数组派生而来的序列,删除(或不删除)数组中的元素而不改变其余元素的顺序。例如,[3,6,2,7] 是数组 [0,3,1,6,2,2,7] 的子序列,但[3,0]就不是子序列。

2. 最长子序列

2.1 最长递增子序列

题目:
给你一个整数数组 nums ,找到其中最长严格递增子序列的长度。
例如:
输入:nums = [10,9,2,5,3,7,101,18]
输出:4
解释:最长递增子序列是 [2,3,7,101],因此长度为 4

输入:nums = [7,7,7,7,7,7,7]
输出:1

思路一:

  1. dp[i]用来表示以arr[i]结尾的最长递增子序列,
  2. dp[0]=1
  3. dp[i] 和 dp[i-1] 之间的关系,还是和之前dp数据都有关系?
    1. 如果 arr[i] > arr[0]...arr[k]...arr[i-1] 那么对应dp[i] = dp[k] + 1,然后求最大值,即dp[i] = max{dp[k]+1}
    2. 如果 arr[i] <= arr[k],dp[i] = max{dp[k]}
  4. 返回Max{dp[0 ~ arr.length-1]}就是最长递增子序列的答案

时间复杂度:O(n^2)

private int process(int[] arr){
        //1. dp[i] 以arr[i]为结尾的最长的递增子序列的长度
        int[] dp = new int[arr.length+1];
        //2. 边界
        dp[0] = 1;
        //3. 转移方程
        int max = Integer.MIN_VALUE;
        for (int i = 1; i < arr.length; i++) {
            int maxLength = Integer.MIN_VALUE;
            for (int k = 0; k < i; k++) {
                if(arr[i] > arr[k]){
                    maxLength = Math.max(maxLength, dp[k] + 1);
                }else {
                    maxLength = Math.max(maxLength, 1);
                }
            }
            dp[i] = maxLength;
            max = Math.max(max, maxLength);
        }
        return max;
    }

思路2:

  1. 使用dp数组来记录当前递增子序列的元素,那么dp[k] 为长度为k+1的递增子序列的末尾的元素
  2. 遍历arr数组,在dp中寻找arr[i]的位置:index
  3. 如果index = dp中当前元素的数量,那么dp中增加arr[i],即dp[index] = arr[i], 同时更新maxLength
  4. 如果index不等于dp中当前元素的数量(即不是dp的末尾位置),那么也更新dp[index]=arr[i]
  5. 最后返回maxLength
private int processUpgrade(int[] arr){
        //1. dp[k] 为长度为k+1的递增子序列的末尾的元素
        int[] dp = new int[arr.length];
        //2.
        int maxLength = 0;
        for (int i = 0; i < arr.length; i++) {
            //在dp中二分查找arr[i]的位置
            int index = findIndex(dp, maxLength, arr[i]);
            //如果index和当前的最大长度相等,符合递增条件,那么就把maxLength+1,并记录当前递增子序列的末尾元素(最大元素)
            if(index == maxLength){
                maxLength++;
                dp[index] = arr[i];
            }else {
                dp[index] = arr[i];
            }
        }
        return maxLength;
    }


    private int findIndex(int[] dp, int length, int target){
        int s = 0;
        int e = length-1;
        while (e >= s){
            int mid = (s + e)/2;
            if(target > dp[mid]){
                s = mid + 1;
            }else if(target < dp[mid]) {
                e = mid - 1;
            }else{
                return mid;
            }
        }
        return s;
    }

2.2 最长递增+1子序列

题目:
给你一个整数数组 nums ,找到其中最长严格递增+1子序列的长度。
例如:
输入:nums = [10,9,1,2,4,7,3]
输出:3
解释:最长递增子序列是 [1,2,3],因此长度为 3

思路:

  1. 遍历数据,保存Map<arr[i], index> map1,即 k=10,v=0;k=9,v=1;如果有重复元素,按新index覆盖
  2. 遍历数据,记录Map<arr[i], length> map2, 其中length为arr[i]结尾的最长严格递增子序列的长度
    1. 从map1中找arr[i]-1,如果存在且下标为index,判断i>index, 记录:map2<arr[i], length+1>,含义为以arr[i]结尾的最长最长严格递增子序列的长度加了1
    2. 同时在遍历的过程中更新maxLength,
      3.遍历完返回maxLength就是最长严格递增+1子序列的长度
      4.同时,如果要求返回数组的话就遍历map2,找到v=maxLength的记录<k,v>,结果就是:[k-maxLength+1, k];如果有多条相同的记录就是List<[k-maxLength+1, k]>
public int process(int[] arr){
        Map<Integer, Integer> map1 = new HashMap<>();
        for (int i = 0; i < arr.length; i++) {
            map1.put(arr[i], i);
        }
        int maxLength = 0;
        for (int i = 0; i < arr.length; i++) {
            int tmpLength = 0;
            if(map1.containsKey(arr[i]-1)){
                Integer index = map1.get(arr[i] - 1);
                if(i > index){
                    tmpLength++;
                }
            }
            maxLength = Math.max(maxLength, tmpLength);
        }
        return maxLength;
    }

    public List<List<Integer>> processGetSubArr(int[] arr){
        Map<Integer, Integer> map1 = new HashMap<>();
        for (int i = 0; i < arr.length; i++) {
            map1.put(arr[i], i);
        }
        int maxLength = 0;
        Map<Integer, Integer> map2 = new HashMap<>();
        for (int i = 0; i < arr.length; i++) {
            int tmpLength = 0;
            if(map1.containsKey(arr[i]-1)){
                Integer index = map1.get(arr[i] - 1);
                if(i > index){
                    tmpLength++;
                    map2.put(arr[i], tmpLength);
                }
            }else {
                map2.put(arr[i], 1);
            }
            maxLength = Math.max(maxLength, tmpLength);
        }


        List<List<Integer>> result = new ArrayList<>();
        for (Map.Entry<Integer, Integer> entry : map2.entrySet()) {
            if(entry.getValue() == maxLength){
                List<Integer> tmp = new ArrayList<>();
                for (int i = entry.getKey(); i >= entry.getKey() - maxLength + 1 ; i--) {
                    tmp.add(i);
                }
                result.add(tmp);
            }
        }
        return result;
    }

2.3 最长递增子序列2