动态规划+哈希LeetCode-1218.最长定差子序列

给你一个整数数组 arr 和一个整数 difference,请你找出并返回 arr 中最长等差子序列的长度,该子序列中相邻元素之间的差等于 difference 。

子序列 是指在不改变其余元素顺序的情况下,通过删除一些元素或不删除任何元素而从 arr 派生出来的序列。

示例 1:

输入:arr = [1,2,3,4], difference = 1
输出:4
解释:最长的等差子序列是 [1,2,3,4]。

示例 2:

输入:arr = [1,3,5,7], difference = 1
输出:1
解释:最长的等差子序列是任意单个元素。

示例 3:

输入:arr = [1,5,7,8,5,3,4,2,1], difference = -2
输出:4
解释:最长的等差子序列是 [7,5,3,1]。

提示:

  • 1 <= arr.length <= 10^5
  • -10^4 <= arr[i], difference <= 10^4

遇到最值问题,我们可以往动态规划方法去想一想,此题可以用动态规划来进行解题,但这题还需要加上哈希表。动规五部曲(dp含义、递推公式、初始化、遍历顺序、打印数组) ,主要的就是把大问题拆解成小问题,小问题的值保存下来,从小问题的答案推导到最终答案,空间换时间。

题目及思路:这题 一开始我把dp[i]的含义定义为arr数组每个位置i所能形成的最长定差子序列长度,想的是使用双重循环遍历每个i和j<i的情况,检查是否能将arr[i]接在arr[j]后面形成更长的定差子序列,从而更新dp[i]的值,但这样时间复杂度是O(n²),导致超时。

那只能优化这个思路,改用更高效的方法。使用哈希表来记录每个数值对应的最长序列长度。这样,对于每个元素num(arr[i]),只需要查找前驱值prev = num - difference是否存在,就能直接得到前面的长度,不需要遍历所有之前的元素。这样可以将时间复杂度降低到O(n)。

然而c语言没有直接能使用哈希表,所以我们可以创建dp数组dp[num],其中的num就是键值,这里的键值代表每个数值,dp[num]所得到的值表示以值 num 结尾的最长等差子序列长度。

这题我们需要特别注意看一下范围,看得出我们在查找prev = num - difference是否存在时会出现负数,从而可能导致下标为负数,所以我们先把num偏移到非负数,再计算前驱值prev。如果dp[prev]>0说明前驱值存在,则dp[num] = dp[prev]+1,else dp[num] = 1;(当前元素单独成序列)

dp含义:dp[num]表示以值 num 结尾的最长等差子序列长度。

初始化:int dp[200001]={0};初始化全为0.

递推公式:dp[num] = dp[prev] + 1;

遍历顺序:从前往后

打印数组:当遇到疑惑或者提交错误时,打印数组出来比较快速的看看哪一步有错。

这是没加100000的遍历过程,以方便大家观看和理解

以下是我在力扣c语言提交的代码,仅供参考:
 

int longestSubsequence(int* arr, int arrSize, int difference) {
    // 初始化为全0
    int dp[200001]={0};

    int max = 0;

    for(int i = 0;i<arrSize;i++)
    {
        int num = 0;
        // 偏移到非负数
       num = arr[i] + 100000;
       int prev = num - difference;
       // 检查 prev 是否在合法范围内和前驱值是否存在
       if(prev >= 0 && prev < 200001 && dp[prev] > 0)
       {
         dp[num] = dp[prev] + 1;
       }
       else
       {
         // 前驱不存在,独立成序列
         dp[num] = 1;
       } 

       if(dp[num] > max)
       {
        max = dp[num];
       }
    }
    return max;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值