记2831.找出最长等值子数组 练习理解
给你一个下标从 0 开始的整数数组
nums
和一个整数k
。如果子数组中所有元素都相等,则认为子数组是一个 等值子数组 。注意,空数组是 等值子数组 。
从
nums
中删除最多k
个元素后,返回可能的最长等值子数组的长度。子数组 是数组中一个连续且可能为空的元素序列。
思路:
题目意思是去找最大相同元素的数组,可以去找数组内可存在最多k个不同数字的子数组。而要去实现这一操作可以暴力枚举每一个存在的数可以存在最长的数组长度但时间复杂度相对大。
优化一下:可以用哈希表和滑窗共同解决,先用哈希表去找到所有数字出现的位置(在列表中的索引),一般是去找值的位置,而这道题需要统计同一个数字出现的索引位置,然后利用滑窗的办法去遍历每个数字所出现的索引位置(利用所有相同位置的索引位置的差就j[right]-j[left]代表此时数组间有多少元素,再去减去此数组1的个数right-left即为可删去的k的多少
from typing import List
from collections import defaultdict
class Solution:def longestEqualSubarray(self, nums: List[int], k: int) -> int:max_len=0pos=defaultdict(list)for i , char in enumerate(nums):pos[char].append(i)for j in pos.values():left=0for right in range(len(j)):count=j[right]-j[left]-(right-left)while count>k:left+=1count=j[right]-j[left]-(right-left)max_len=max(max_len,right-left+1)return max_len
举个例子就可以更好的理解:
以
nums = [1,2,1,1,3,3]
中数字 1 的位置列表[0,2,3]
为例:
初始状态:left=0,right 从 0 开始遍历
- right=0(位置 0):
- 总长度 = 0-0+1=1,x 的数量 = 1
- 需要删除 = 1-1=0 ≤ k=2,窗口有效
- 窗口长度 = 1,max_len=1
right=1(位置 2):
- 总长度 = 2-0+1=3,x 的数量 = 2
- 需要删除 = 3-2=1 ≤ k=2,窗口有效
- 窗口长度 = 2,max_len=2
right=2(位置 3):
- 总长度 = 3-0+1=4,x 的数量 = 3
- 需要删除 = 4-3=1 ≤ k=2,窗口有效
- 窗口长度 = 3,max_len=3
当需要删除的元素超过 k 时
假设
k=0
(不允许删除元素),处理positions = [0,2,3]
:
- right=2(位置 3)时:
- 需要删除 = 4-3=1 > k=0,窗口无效
- 移动 left 到 1,此时窗口为 [1,2]
- 重新计算:总长度 = 3-2+1=2,x 的数量 = 2
- 需要删除 = 2-2=0 ≤ 0,窗口有效
- 窗口长度 = 2
总结窗口移动规则
- 右指针 right:依次遍历每个位置,扩大窗口范围
- 左指针 left:当窗口需要删除的元素超过 k 时,右移 left 缩小窗口
- 有效性保证:每次 right 移动后,都通过调整 left 确保窗口有效
- 长度更新:在窗口有效后,立即用当前窗口长度更新 max_len
难点:理解更新思路以及滑窗更新判断依据
将问题转化为位置列表处理
- 难点:直接在原始数组上滑动窗口操作难以高效计算需要删除的元素
- 解决思路:先记录每个数字的所有出现位置,将问题转化为对每个位置列表的处理,简化了计算
计算需要删除的元素数量
- 难点:如何快速计算窗口中需要删除的元素(非目标数字)
- 关键公式:
需要删除的元素 = 窗口覆盖的总长度 - 目标数字的数量
- 具体计算:
positions[right] - positions[left] + 1 - (right - left + 1)