最长增长子序列 LIS

给定一个长度为N的数组,找出一个最长的单调递增子序列。

例如:给定数组A{5,6,7,1,2,8},则其最长的单调递增子序列为{5,6,7,8},长度为4。

一种思路是将其转换成LCS问题:将A数组排序后:A’{1, 2, 5, 6, 7, 8}

因为,原数组A的子序列顺序保持不变,而且排序后A’本身就是递增的,这样,就保证了两序列的最长公共子序列的递增特性。如此,若想求数组A的最长递增子序列,其实就是求数组A与它的排序数组A‘的最长公共子序列。

此外,本题可以直接使用动态规划求解。

算法如下:

设长度为N的数组为{a0,a1, a2, …an-1},则 假定以aj结尾的数组序列的最长递增子序列 长度为L(j),则L(j)={ max(L(i))+1, i

package other;

import java.lang.reflect.Array;

import util.ArrayUtil;

public class LongestIncreaseingSubSequence {

    public static void main(String[] args) {
        int[] a = new int[]{5,2,8,6,3,6,9,7};
        System.out.println("最长递增子序列长度:"+longestIncreaseingSubSequence(a));
    }

    public static int longestIncreaseingSubSequence(int[] a){
        int[] L = new int[a.length];
        int[] S = new int[a.length];
        int re = 0;
        int rei= 0;
        for (int i = 0; i < L.length; i++) {
            //max 为a[0]到a[i]的最长公共子序列     并不定是最后返回值
            int max = 1;
            for (int j = i-1; j >=0; j--) {
                int m = 1;
                if(a[j]<a[i]&&max<L[j]+1){
                    max = L[j]+1;
                    S[i] = j;
                }
            }
            L[i] = max;
            if(re<L[i]){
                re = L[i];
                rei = i;
            }
        }

        ArrayUtil.display("序列元素",a);
        ArrayUtil.display("子序列长度",L);
        ArrayUtil.display("前面的 数组序列号",S);
        System.out.print("最长递增子序列为:");
        display(a,S,rei);
        System.out.println();
        return re;
    }

    public static void display(int[] a,int[] S,int rei){
        if(S[rei]!=0){
            display(a,S,S[rei]);
        }
        System.out.print(a[rei]+" ");       
    }
}

这里写图片描述

<think>好的,我现在需要解决用户关于最长递增子序列LIS)的问题。用户希望了解解决方法或算法实现,特别是LIS算法的具体实现和解释。首先,我要回忆LIS的基本概念和常见的解决方法。 根据已有的引用内容,引用[1]、[3]和[4]都提到了LIS是一个经典的动态规划问题,其中引用[4]还提供了一个Java代码示例。动态规划方法是解决LIS的常见方法,时间复杂度为$O(n^2)$。此外,可能还有一种更高效的$O(n \log n)$算法,比如使用贪心结合二分查找的方法,但用户提供的引用中没有提到这一点,所以需要确认是否要包括这部分内容。不过,用户提供的引用中主要涉及动态规划,所以可能需要优先解释动态规划方法。 接下来,我需要整理动态规划解法的步骤。根据引用[4]的代码,动态规划数组dp的定义是dp[i]表示以第i个元素结尾的最长递增子序列的长度。初始化时,每个dp[i]都为1,因为每个元素本身就是一个长度为1的子序列。然后,对于每个元素i,遍历它前面的所有元素j,如果nums[i] > nums[j],则更新dp[i]为dp[j]+1的最大值。最后,整个dp数组中的最大值就是LIS的长度。 用户可能需要更详细的步骤解释,比如为什么使用动态规划,状态转移方程的意义,以及如何从dp数组中得到结果。此外,引用中的例子,比如{2,3,7,101}或{2,3,7,18},可以用来举例说明算法过程。 另外,用户提供的引用[2]提到了Vue3的diff算法中使用了LIS,这可能是一个应用场景,可以作为补充说明,但用户的主要问题还是关于算法实现和解释。 需要确保回答结构清晰,分步骤说明,并正确使用LaTeX格式。比如,状态转移方程需要用行内公式,而可能涉及的更复杂的公式需要用独立公式格式。同时,用户要求生成相关问题,需要根据内容提出相关的问题,例如优化方法、应用场景等。 最后,检查是否所有数学表达式都正确使用了$...$或$$...$$格式,引用标识是否在适当的位置添加。例如,在提到动态规划是经典方法时,添加引用[^1]或[^3],在提到代码示例时引用[^4]。</think>### 最长递增子序列LIS)算法解释与实现 #### 1. 问题定义 给定一个整数序列,找到其中最长的递增子序列子序列不要求连续,但元素顺序必须保持原序列顺序,且严格递增)。例如,序列$[2,5,3,7,11,8,10]$的最长递增子序列为$[2,3,7,8,10]$,长度为5。 --- #### 2. 动态规划解法($O(n^2)$) **核心思想**: 定义$dp[i]$表示以第$i$个元素结尾的最长递增子序列长度。通过比较当前元素与之前所有元素的关系更新$dp$数组[^1][^4]。 **算法步骤**: 1. **初始化**:$dp$数组所有元素初始化为1(每个元素自身构成长度为1的子序列) 2. **状态转移**: 对每个$i$(从1到$n-1$),遍历$j < i$: $$ \text{若}\ nums[i] > nums[j],\ \text{则}\ dp[i] = \max(dp[i], dp[j]+1) $$ 3. **取最大值**:最终结果为$dp$数组中的最大值 **代码实现**(Python): ```python def length_of_lis(nums): dp = [1] * len(nums) max_len = 1 for i in range(1, len(nums)): for j in range(i): if nums[i] > nums[j]: dp[i] = max(dp[i], dp[j]+1) max_len = max(max_len, dp[i]) return max_len ``` **示例分析**: 对于序列$[10,9,2,5,3,7,101,18]$: - 计算$dp[3]$(对应元素5)时,发现5 > 2(对应$dp[2]=1$),因此$dp[3]=2$ - 最终$dp$数组为$[1,1,1,2,2,3,4,4]$,最长长度为4,对应子序列$[2,5,7,101]$或$[2,5,7,18]$ --- #### 3. 优化解法($O(n \log n)$) **核心思想**: 维护一个递增序列`tail`,使用二分查找快速定位插入位置。 - `tail[i]`表示长度为$i+1$的递增子序列的最小末尾值 - 通过贪心策略尽可能让序列增长更慢 **算法步骤**: 1. 初始化空列表`tail` 2. 遍历每个元素,用二分查找确定其在`tail`中的位置: - 若元素大于所有`tail`值,则追加到末尾 - 否则替换第一个大于它的元素 **代码实现**(Python): ```python import bisect def length_of_lis_optimized(nums): tail = [] for num in nums: idx = bisect.bisect_left(tail, num) if idx == len(tail): tail.append(num) else: tail[idx] = num return len(tail) ``` --- #### 4. 应用场景 - **算法竞赛**:二维LIS问题(如信封嵌套问题)[^1] - **前端框架**:Vue3的diff算法利用LIS优化DOM更新顺序[^2] - **生物信息学**:DNA序列比对中的最长公共子序列问题 ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值