LeetCode 220 存在重复元素 III 题解
LeetCode 220 存在重复元素 III 题解
题目描述
给定一个整数数组 nums 和两个整数 k 和 t,请判断数组中是否存在两个不同的索引 i 和 j,使得:
abs(nums[i] - nums[j]) <= t
abs(i - j) <= k
方法思路:桶排序 + 滑动窗口
核心思想
-
桶排序思想:将数值范围划分为大小为
t+1
的桶-
每个元素根据值分配到对应的桶(编号 = 值 // (t+1))
-
同一桶内的元素自然满足差值条件
-
相邻桶的元素需要额外验证差值,
相邻桶要验证额外差值的具体原因和实现逻辑:1.1 桶的划分方式 每个桶的宽度是 t + 1 (w = t + 1),桶编号通过 数值 // w 计算得到。这种划分方式导致:
- 同一桶内元素的差值 一定 ≤ t (自动满足条件)- 相邻桶中元素 可能满足差值 ≤ t
1.2. 边界情况示例 假设 t=3(w=4),数值分布:
- 桶0:[0-3]- 桶1:[4-7]- 桶-1:[-4--1]当出现:- 数值3(桶0)和4(桶1) → 差1 ≤ 3- 数值-1(桶-1)和0(桶0) → 差1 ≤ 3
1.3 必须显式验证的原因 相邻桶中的元素不一定满足差值条件(比如桶0的3和桶1的7差4>3),因此需要通过 abs(nums[i] - bucket_value) < w 的二次验证,确保实际差值 ≤ t
1.4. 算法完整性 这种设计保证三种可能性都被覆盖:- ✅ 同桶元素 → 直接返回- ✅ 左邻桶元素 → 验证后返回- ✅ 右邻桶元素 → 验证后返回
-
-
滑动窗口:维护大小为k的窗口
- 使用字典记录当前窗口内的元素
- 当窗口超过k个元素时,删除最早进入的元素
算法步骤
- 处理非法输入(t < 0)
- 初始化桶字典和桶宽度
w = t + 1
- 遍历数组元素:
- 计算当前元素的桶编号
- 检查当前桶和相邻桶是否满足条件
- 维护滑动窗口大小(删除过期元素)
复杂度分析
- 时间复杂度:O(n),每个元素处理时间为O(1)
- 空间复杂度:O(min(n,k)),滑动窗口最多保存k个元素
关键点说明
-
桶宽度选择:
t+1
的宽度保证了:- 同一桶内元素差≤t
- 相邻桶元素需要显式验证差值
-
滑动窗口维护:
if i >= k:expired_bucket = nums[i - k] // wdel bucket[expired_bucket]
这保证了任何时候桶中只包含最近k个元素
- 负数处理 :Python的整数除法是向下取整,例如:
- -3 // 4 = -1 (而正数3//4=0)
- 这确保了相邻负数的正确分桶
示例输入
nums = [1, 5, 9, 1, 5, 9]
k = 2
t = 3
输出应为False,因为虽然存在相同元素,但索引差超过k的限制
LeetCode 220 存在重复元素 III
https://leetcode.cn/problems/7WqeDu/
解法:桶排序 + 滑动窗口 时间复杂度O(n) 空间复杂度O(min(n,k))
# LeetCode 220 存在重复元素 III
# https://leetcode.cn/problems/7WqeDu/
# 解法:桶排序 + 滑动窗口 时间复杂度O(n) 空间复杂度O(min(n,k))from typing import Listclass Solution:def containsNearbyAlmostDuplicate(self, nums: List[int], k: int, t: int) -> bool:"""判断数组中是否存在满足以下条件的两个不同索引i,j:1. abs(nums[i] - nums[j]) <= t2. abs(i - j) <= k参数:nums: 输入数组k: 索引最大差值(滑动窗口大小)t: 数值最大差值返回:bool: 是否存在满足条件的元素对"""if t < 0: # 处理非法输入,t不能为负数return Falsen = len(nums)bucket = {} # 用字典维护桶,键为桶编号,值为桶中元素值w = t + 1 # 桶的宽度(避免t=0时除零错误)for i in range(n):# 计算当前元素所属的桶编号bucket_id = nums[i] // w # 整数除法自动向下取整# 检查当前桶是否已有元素(保证数值差<=t)if bucket_id in bucket:return True# 检查左侧相邻桶(需要额外验证数值差)if (bucket_id - 1) in bucket and abs(nums[i] - bucket[bucket_id - 1]) < w:return True# 检查右侧相邻桶(需要额外验证数值差)if (bucket_id + 1) in bucket and abs(nums[i] - bucket[bucket_id + 1]) < w:return True# 将当前元素加入桶bucket[bucket_id] = nums[i]# 维护滑动窗口,当i>=k时删除最早进入桶的元素if i >= k:expired_bucket = nums[i - k] // wdel bucket[expired_bucket]return Falseif __name__ == "__main__":# 测试用例(题目示例)nums = [1, 5, 9, 1, 5, 9]k = 2t = 3print(Solution().containsNearbyAlmostDuplicate(nums, k, t)) # 预期输出:False