最长和谐子序列的变种:最多允许k次不和谐的元素
字数 999 2025-11-23 08:09:31

最长和谐子序列的变种:最多允许k次不和谐的元素

题目描述

给定一个整数数组nums和一个整数k,我们需要找到最长的和谐子序列的长度。和谐子序列定义为:序列中最大值和最小值的差恰好为1。但是在这个变种问题中,我们允许最多k个元素不满足这个条件(即这些元素的值与序列中其他元素的差值可以大于1)。

例如:

  • nums = [1,3,2,2,5,2,3,7], k = 1
  • 输出:5
  • 解释:最长和谐子序列可以是[3,2,2,2,3],其中最大值3和最小值2的差为1,且只有一个元素5不满足条件(k=1)

解题过程

步骤1:理解问题核心

和谐子序列要求序列中最大值和最小值的差正好为1。在变种问题中,我们允许最多k个"异常值"(这些元素的值不在[min, min+1]范围内)。

关键观察:

  1. 和谐序列实际上只包含两种数值:x和x+1
  2. 异常值可以是任何其他数值
  3. 我们需要找到一对相邻数值(x, x+1),使得包含这对数值的所有元素数量加上最多k个异常值能达到最大长度

步骤2:排序简化问题

由于我们关心的是数值本身而非原始顺序,可以先将数组排序:

sorted_nums = sorted(nums)

排序后,相同数值的元素会聚集在一起,便于我们统计频率和滑动窗口处理。

步骤3:频率统计

统计每个数值出现的频率:

# 例如 nums = [1,3,2,2,5,2,3,7], k = 1
# 频率统计:
# 1: 1次, 2: 3次, 3: 2次, 5: 1次, 7: 1次

步骤4:滑动窗口方法

我们使用滑动窗口来找到最优的数值对(x, x+1):

  1. 定义左指针left和右指针right
  2. 维护当前窗口中的数值集合
  3. 计算窗口内满足条件的元素数量

具体算法:

def findLHS(nums, k):
    nums.sort()
    left = 0
    max_length = 0
    counter = {}
    
    for right in range(len(nums)):
        # 更新当前数值的频率
        counter[nums[right]] = counter.get(nums[right], 0) + 1
        
        # 检查当前窗口是否有效
        while max(counter.keys()) - min(counter.keys()) > 1:
            # 窗口无效,移动左指针
            counter[nums[left]] -= 1
            if counter[nums[left]] == 0:
                del counter[nums[left]]
            left += 1
        
        # 计算当前窗口的有效长度
        current_length = right - left + 1
        max_length = max(max_length, current_length)
    
    return min(max_length, len(nums))  # 确保不超过数组长度

但上面的代码没有正确处理k个异常值的情况,我们需要改进。

步骤5:处理异常值的改进算法

更精确的算法:

def findLHSWithKExceptions(nums, k):
    nums.sort()
    left = 0
    max_length = 0
    exception_count = 0
    
    for right in range(len(nums)):
        # 如果当前元素与窗口最小值差值大于1,增加异常计数
        if nums[right] - nums[left] > 1:
            exception_count += 1
        
        # 当异常值超过k时,移动左指针
        while exception_count > k:
            if nums[left + 1] - nums[left] > 0:  # 如果左边界是较小值
                exception_count -= 1
            left += 1
        
        # 更新最大长度
        max_length = max(max_length, right - left + 1)
    
    return max_length

步骤6:最终优化解法

更高效的解法是分别考虑每对相邻数值(x, x+1):

def findLHSWithKExceptions(nums, k):
    from collections import Counter
    
    count = Counter(nums)
    unique_nums = sorted(count.keys())
    max_length = 0
    
    for i in range(len(unique_nums) - 1):
        if unique_nums[i + 1] - unique_nums[i] == 1:
            # 这对相邻数值可以形成和谐子序列的基础
            base_length = count[unique_nums[i]] + count[unique_nums[i + 1]]
            
            # 计算可以添加的异常值数量
            remaining_k = k
            current_length = base_length
            
            # 尝试向两边扩展,添加异常值
            # 向左扩展(小于min的值)
            j = i - 1
            while j >= 0 and remaining_k > 0:
                can_add = min(remaining_k, count[unique_nums[j]])
                current_length += can_add
                remaining_k -= can_add
                j -= 1
            
            # 向右扩展(大于max的值)
            j = i + 2
            remaining_k = k - (base_length - current_length)  # 重置剩余k值
            while j < len(unique_nums) and remaining_k > 0:
                can_add = min(remaining_k, count[unique_nums[j]])
                current_length += can_add
                remaining_k -= can_add
                j += 1
            
            max_length = max(max_length, current_length)
    
    return max_length

步骤7:动态规划思路(备选)

我们也可以用动态规划来解决:

def findLHSWithKExceptions_DP(nums, k):
    nums.sort()
    n = len(nums)
    
    # dp[i][j] 表示以nums[i]结尾,使用j个异常值的最长和谐子序列长度
    dp = [[1] * (k + 1) for _ in range(n)]
    max_len = 1
    
    for i in range(1, n):
        for j in range(k + 1):
            for prev in range(i):
                diff = nums[i] - nums[prev]
                exceptions_used = 0 if diff <= 1 else 1
                
                if j >= exceptions_used:
                    if diff <= 1 or exceptions_used == 1:
                        dp[i][j] = max(dp[i][j], dp[prev][j - exceptions_used] + 1)
            
            max_len = max(max_len, dp[i][j])
    
    return max_len

步骤8:复杂度分析

  • 排序:O(n log n)
  • 滑动窗口:O(n)
  • 空间复杂度:O(n) 用于存储频率计数

步骤9:示例验证

以nums = [1,3,2,2,5,2,3,7], k = 1为例:

  1. 排序后:[1,2,2,2,3,3,5,7]
  2. 考虑数值对(2,3):基础长度 = 3(个2) + 2(个3) = 5
  3. 剩余k=1,可以添加1个异常值(5或1)
  4. 最终得到长度6:[1,2,2,2,3,3] 或 [2,2,2,3,3,5]

这个解法能够有效地找到最长的和谐子序列,同时允许最多k个异常元素。

最长和谐子序列的变种:最多允许k次不和谐的元素 题目描述 给定一个整数数组nums和一个整数k,我们需要找到最长的和谐子序列的长度。和谐子序列定义为:序列中最大值和最小值的差恰好为1。但是在这个变种问题中,我们允许最多k个元素不满足这个条件(即这些元素的值与序列中其他元素的差值可以大于1)。 例如: nums = [ 1,3,2,2,5,2,3,7 ], k = 1 输出:5 解释:最长和谐子序列可以是[ 3,2,2,2,3 ],其中最大值3和最小值2的差为1,且只有一个元素5不满足条件(k=1) 解题过程 步骤1:理解问题核心 和谐子序列要求序列中最大值和最小值的差正好为1。在变种问题中,我们允许最多k个"异常值"(这些元素的值不在[ min, min+1 ]范围内)。 关键观察: 和谐序列实际上只包含两种数值:x和x+1 异常值可以是任何其他数值 我们需要找到一对相邻数值(x, x+1),使得包含这对数值的所有元素数量加上最多k个异常值能达到最大长度 步骤2:排序简化问题 由于我们关心的是数值本身而非原始顺序,可以先将数组排序: 排序后,相同数值的元素会聚集在一起,便于我们统计频率和滑动窗口处理。 步骤3:频率统计 统计每个数值出现的频率: 步骤4:滑动窗口方法 我们使用滑动窗口来找到最优的数值对(x, x+1): 定义左指针left和右指针right 维护当前窗口中的数值集合 计算窗口内满足条件的元素数量 具体算法: 但上面的代码没有正确处理k个异常值的情况,我们需要改进。 步骤5:处理异常值的改进算法 更精确的算法: 步骤6:最终优化解法 更高效的解法是分别考虑每对相邻数值(x, x+1): 步骤7:动态规划思路(备选) 我们也可以用动态规划来解决: 步骤8:复杂度分析 排序:O(n log n) 滑动窗口:O(n) 空间复杂度:O(n) 用于存储频率计数 步骤9:示例验证 以nums = [ 1,3,2,2,5,2,3,7 ], k = 1为例: 排序后:[ 1,2,2,2,3,3,5,7 ] 考虑数值对(2,3):基础长度 = 3(个2) + 2(个3) = 5 剩余k=1,可以添加1个异常值(5或1) 最终得到长度6:[ 1,2,2,2,3,3] 或 [ 2,2,2,3,3,5 ] 这个解法能够有效地找到最长的和谐子序列,同时允许最多k个异常元素。