220 存在重复元素 III(分桶、滑动窗口)

1. 问题描述:

在整数数组 nums 中,是否存在两个下标 i 和 j,使得 nums [i] 和 nums [j] 的差的绝对值小于等于 t ,且满足 i 和 j 的差的绝对值也小于等于 ķ 。如果存在则返回 true,不存在返回 false。

示例 1:

输入: nums = [1,2,3,1], k = 3, t = 0
输出: true

示例 2:

输入: nums = [1,0,1,1], k = 1, t = 2
输出: true

示例 3:

输入: nums = [1,5,9,1,5,9], k = 2, t = 3
输出: false
来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/contains-duplicate-iii

2. 思路分析:

① 这道题目与219题存在重复元素是类似的,但是219的题目是找出元素是一样的并且要求两个元素之间的距离小于等于k的,219题直接使用字典来存储相同的数字即可判断出是否存在两个这样的元素,但是这道题目是不一样的元素,一开始的时候没有什么思路但是后面看了一下力扣的思路,感觉比较好的解决方法是使用分桶的思想,python语言可以使用字典来实现,下面是对于分桶思路的理解:

② 分桶的思路与219题的思路还是比较类似的,219题可以看成相等的数字是放在了一个桶,只是在循环中直接可以判断出两个相等的元素,这里就可以将nums[i] // (t + 1)的方式进行分桶,也就是将绝对值小于等于t的放在了一个桶中,这个表达式其实也好理解,表示当前的数字应该落在了哪一个桶中,主要存在以下几种情况:

(1) 落在一个桶中:比如10,11,t = 2相差小于等于t那么肯定是落在了相同的桶中这个时候肯定是满足绝对值小于等于t的

(2) 落在相邻的桶中:比如相邻的数字为5,8,t = 2那么 5 // 2 = 2落在了第2个桶, 8 // 2落在了第4个桶,距离超出了t的那么肯定是落在了不同的桶中,这个时候是不满足绝对值小于等于t的

还有另外一种情况比如相邻的数字为5,6, t = 2,分别落在了第2与第3个桶中,但是这个绝对值是小于等于t的所以落在相邻的桶的时候需要判断一下绝对值是否小于等于t假如满足这两个元素也是符合条件的

(3) 假如不是落在相邻的桶那么肯定是不满足绝对值小于等于t的,这个时候就需要判断桶中元素是否超过了k,超过则需要将最开始放入桶中的元素进行删除,这样在循环中桶会一直保持k个元素

③ 除了上面的思路之外,由题目可以知道我们其实在遍历nums的时候可以维护一个长度为k的集合s(很多时候根据题目画图会更好理解),集合s中的元素由小到大排序,当遍历到nums[i]的时候那么在集合s中找到第一个大于等于nums[i]的下标index判断这个下标对应的值与nums[i]的值的绝对值是否小于等于t,如果小于等于t那么返回True,否则index减一,减一的目的是为了找到小于等于nums[i]的下标,然后比较nums[index]与nums[i]的绝对值是否小于等于t,如果小于等于t返回True,最后将nums[i]加入到集合s中。因为需要维护集合中元素的顺序,所以可以使用python中的bisect模块查找与插入对应的元素,这样就可以维护集合s中元素的顺序。

3. 代码如下:

大佬的代码如下:

class Solution:
    def containsNearbyAlmostDuplicate(self, nums: List[int], k: int, t: int) -> bool:
        bucket = dict()
        if t < 0: return False
        for i in range(len(nums)):
            # 当前数字应该在哪一个桶
            nth = nums[i] // (t + 1)
            if nth in bucket:
                # 分在了之前已经存在的桶说明肯定是绝对值小于等于t的
                return True
            # 判断落入相邻的桶中的元素是否满足绝对值小于等于t的(相邻的桶也是有可能满足条件的)
            if nth - 1 in bucket and abs(nums[i] - bucket[nth - 1]) <= t:
                return True
            if nth + 1 in bucket and abs(nums[i] - bucket[nth + 1]) <= t:
                return True
            bucket[nth] = nums[i]
            # 假如上面的都不满足这个时候字典中的元素为k的时候需要将一开始的元素删除掉
            if i >= k: bucket.pop(nums[i - k] // (t + 1))
        return False

根据y总的视频讲解思路修改的python代码,c++语言使用的是lower_bound函数来维护集合s的有序性,python语言使用的是bisect模块:

from typing import List
import bisect


class Solution:
    def containsNearbyAlmostDuplicate(self, nums: List[int], k: int, t: int) -> bool:
        s = list()
        # 实际上是维护一个长度为k的窗口
        i, j = 0, 0
        while i < len(nums):
            # 集合s的长度超过了k那么就要弹出一开始加入的元素
            if i - j > k:
                # 首先是需要找到这个元素的下标
                index = bisect.bisect_left(s, nums[j])
                if index < len(s):
                    s.pop(index)
                    j += 1
            # 只有当集合s不为空的时候查找才有意义
            if s:
                x = nums[i]
                # 找到第一个大于等于x的元素下标
                index = bisect.bisect_left(s, x)
                if index < len(s) and abs(s[index] - x) <= t: return True
                # 减一的目的是为了得到一个小于等于x的元素下标
                index -= 1
                if index >= 0 and abs(s[index] - x) <= t: return True
            # 插入元素的时候维护有序性
            bisect.insort_left(s, nums[i])
            i += 1
        return False

 

你可能感兴趣的:(力扣,滑动窗口)