上升或連續最長子序列問題

最長上升子序列

在這裏插入圖片描述

我們用

dp[i] 表示數組中索引爲 i 結尾的子數組的最長上升子序列的長度

那麼我們要求整體最長子序列就是

dp[nums.length - 1]

分析子問題

dp[nums.length - 1]的子問題就是dp[nums.length - 2],以此類推

分析狀態轉移,怎麼由子問題推導得到原問題的最優解

題目意思就是要我們求得最長上升子序列的長度,對於子序列跟子數組的不同之處就在於,子序列可以跳過,元素和元素在數組中不一定是連續排列的,比如

[2,5,3,7,101]是子序列
[10,101]也是子序列

題目讓我們求最長的一個上升子序列的長度

我們先遍歷所有子數組

for(int i = 0;i < nums.length;i++){
    for(int j = 0;j < i;j++){
        //以上表達的是區間[j,i]的子數組
    }
}

條件,上升

對於子問題如何能得到原問題

這就涉及到如何判斷上升這個條件了,顯然,這個很簡單

nums[i] > nums[j]  
    nums[i] 可以接在 nums[j]之後(此題要求嚴格遞增),此情況下最長上升子序列長度爲 dp[j]+1
    
nums[i] <= nums[j]
    nums[i] 無法接在 nums[j] 之後,此情況上升子序列不成立,跳過。

所以:

    public int lengthOfLIS(int[] nums) {
        int len = nums.length;
        if(len == 0){
            return 0;
        }
        int[] dp = new int[len];
        int maxLen = 1;
        for(int i = 0;i < len;i++){
            int tempLen = 1;
            for(int j = 0;j < i;j++){
                if(nums[i] > nums[j]){
                    tempLen = Math.max(dp[j] + 1,tempLen);
                }
            }
            //記錄以索引i結尾的子數組的最長上升子序列長度
            dp[i] = tempLen;
            //記錄此時的全局最優解
            if(tempLen > maxLen){
                maxLen = tempLen;
            }
        }
        return maxLen;
    }

最長連續遞增序列

在這裏插入圖片描述

這道題和上面的最長上升子序列的區別在於,這道題要求的連續上升,即

temp = 1;//記錄長度
if nums[i] > nums[i - 1]
	temp++	//只有連續遞增長度纔會增加
else
    temp = 1; //否則只能回到初始值,下一次循環

解答:

    /**
     * 最長連續連續遞增序列
     * @param nums
     * @return
     */
    public int findLengthOfLCIS(int[] nums) {
        int max = 1;
        int temp = 1;
        for(int i = 1;i < nums.length;i++){
            if(nums[i] > nums[i - 1]){
                temp++;
                max = Math.max(max,temp);
            }else{
                temp = 1;
            }
        }
        return max;
    }

最長遞增子序列的個數

在這裏插入圖片描述

這道題目和最長上升子序列問題很相似,但是這道題目要求記錄最長上升子序列的組合模式

比如:

[1,3,5,4,7]中[1,3,5,7]和[1,3,4,7]都是最長的上升子序列,長度都爲4,但是有兩種組合模式

難點就在於記錄組合數

所以我們用多一個dp數組來記錄這個組合數

combination[i]:表示以索引i結尾的子數組的最大的最大上升子序列的組合數

combination[i]初始化都賦值爲1,只要有數字,那麼至少都是1。

分析狀態轉移:區間是[ i,j ]

if nums[i] <= nums[j]:
	不是上升序列,直接跳過
else //nums[i] > nums[j]
    if dp[j] + 1 == dp[j]
        //則找到了長度和之前一樣的上升子序列
        combination[i] += combination[j]//更新以i結尾的組合數
    else if  dp[j] + 1 > dp[j] 
        //找到了更長的上升序列
        dp[i] = dp[j] + 1//更新長度
        combination[i] = combination[j];//更新以i結尾的組合數

參考圖:https://leetcode-cn.com/problems/longest-consecutive-sequence/solution/tao-lu-jie-jue-zui-chang-zi-xu-lie-deng-yi-lei-wen/

dbf04caf16370077061aead090c28d8.jpg

    /**
     * 最長遞增子序列數
     * @param nums
     * @return
     */
    public int findNumberOfLIS(int[] nums) {

        if (nums.length == 0) {
            return 0;
        }
        int[] dp = new int[nums.length];
        int[] combination = new int[nums.length];
        Arrays.fill(dp, 1);
        Arrays.fill(combination, 1);
        int max = 1, res = 0;
        for (int i = 1; i < dp.length; i++) {
            for (int j = 0; j < i; j++) {
                if (nums[i] > nums[j]) {
                    if (dp[j] + 1 > dp[i]) {
                        //找到了更長的上升序列
                        dp[i] = dp[j] + 1;  //更新dp[i]長度
                        combination[i] = combination[j];//更新以i結尾的組合數
                    } else if (dp[j] + 1 == dp[i]) {
                        //找到了和之前存的最長序列長度一樣的序列
                        combination[i] += combination[j];//更新組合數
                    }
                }
            }
            max = Math.max(max, dp[i]);
        }
        //統計長度爲最長上升序列長度的子序列組合數
        for (int i = 0; i < nums.length; i++){
            if (dp[i] == max){
                res += combination[i];
            }
        }
        return res;
    }

最長連續序列

在這裏插入圖片描述

最長連續序列和最長連續遞增序列相似,但是這道題元素和元素之間要求連續,即 1 ,2 ,3 ,4…

思路也和最長連續遞增序列相似:

temp = 1;//記錄長度
if nums[i] > nums[i - 1] + 1
	temp++	//只有連續遞增長度纔會增加
else
    temp = 1; //否則只能回到初始值,下一次循環

最長連續遞增序列思路:

temp = 1;//記錄長度
if nums[i] > nums[i - 1]
	temp++	//只有連續遞增長度纔會增加
else
    temp = 1; //否則只能回到初始值,下一次循環

實現

    public int longestConsecutive1(int[] nums) {
        if(nums.length == 0){
            return 0;
        }
        int currentMax = 1;
        int max = 0;
        Arrays.sort(nums);
        for(int i = 1;i < nums.length;i++){
            if(nums[i] != nums[i - 1]){
                if(nums[i] == nums[i - 1] + 1){
                    currentMax++;
                }else{
                    max = Math.max(max,currentMax);
                    currentMax = 1;
                }
            }
        }
        return Math.max(currentMax,max);
    }

最長和諧子序列

在這裏插入圖片描述

這道題目先統計所有元素的出現次數

        //統計各個元素出現次數
        for(int i : nums){
            map.put(i,map.getOrDefault(i,0) + 1);
        }

再遍歷一次數組,以當前遍歷到的元素爲最小值,那麼最大值則是 i+1,判斷map中是否存在該元素,有則計算最大值和最小值的數量

    /**
     * 最長和諧子序列
     * @param nums
     * @return
     */
    public int findLHS(int[] nums) {
        if(nums.length == 0){
            return 0;
        }
        Map<Integer,Integer> map = new HashMap<>();
        //統計各個元素出現次數
        for(int i : nums){
            map.put(i,map.getOrDefault(i,0) + 1);
        }
        int max = 1;
        int temp = 1;
        for(int i : nums){
            //以當前遍歷元素爲最小值,那麼最大值則爲i + 1,判斷map中是否有該元素
            if(map.containsKey(i + 1)){
                temp = map.get(i) + map.get(i + 1);
                max = Math.max(temp,max);
            }
        }
        return max;
    }
發佈了261 篇原創文章 · 獲贊 143 · 訪問量 4萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章