存在重复元素 III

给你一个整数数组 nums 和两个整数 k 和 t 。请你判断是否存在 两个不同下标 i 和 j,使得 abs(nums[i] - nums[j]) <= t ,同时又满足 abs(i - j) <= k 。

如果存在则返回 true,不存在返回 false。

示例:

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

 

提示:

0 <= nums.length <= 2 * 104
-231 <= nums[i] <= 231 - 1
0 <= k <= 104
0 <= t <= 231 - 1

解题思路:

思路及算法

对于序列中每一个元素 xx 左侧的至多 kk 个元素,如果这 kk 个元素中存在一个元素落在区间 [x - t, x + t][x−t,x+t] 中,我们就找到了一对符合条件的元素。注意到对于两个相邻的元素,它们各自的左侧的 kk 个元素中有 k - 1k−1 个是重合的。于是我们可以使用滑动窗口的思路,维护一个大小为 kk 的滑动窗口,每次遍历到元素 xx 时,滑动窗口中包含元素 xx 前面的最多 kk 个元素,我们检查窗口中是否存在元素落在区间 [x - t, x + t][x−t,x+t] 中即可。

如果使用队列维护滑动窗口内的元素,由于元素是无序的,我们只能对于每个元素都遍历一次队列来检查是否有元素符合条件。如果数组的长度为 nn,则使用队列的时间复杂度为 O(nk)O(nk),会超出时间限制。

因此我们希望能够找到一个数据结构维护滑动窗口内的元素,该数据结构需要满足以下操作:

支持添加和删除指定元素的操作,否则我们无法维护滑动窗口;

内部元素有序,支持二分查找的操作,这样我们可以快速判断滑动窗口中是否存在元素满足条件,具体而言,对于元素 xx,当我们希望判断滑动窗口中是否存在某个数 yy 落在区间 [x - t, x + t][x−t,x+t] 中,只需要判断滑动窗口中所有大于等于 x - tx−t 的元素中的最小元素是否小于等于 x + tx+t 即可。

我们可以使用有序集合来支持这些操作。

实现方面,我们在有序集合中查找大于等于 x - tx−t 的最小的元素 yy,如果 yy 存在,且 y \leq x + ty≤x+t,我们就找到了一对符合条件的元素。完成检查后,我们将 xx 插入到有序集合中,如果有序集合中元素数量超过了 kk,我们将有序集合中最早被插入的元素删除即可。

注意

如果当前有序集合中存在相同元素,那么此时程序将直接返回 \texttt{true}true。因此本题中的有序集合无需处理相同元素的情况。

为防止整型 \texttt{int}int 溢出,我们既可以使用长整型 \texttt{long}long,也可以对查找区间 [x - t, x + t][x−t,x+t] 进行限制,使其落在 \texttt{int}int 范围内。

代码

C++JavaGolang

class Solution {
public:
    bool containsNearbyAlmostDuplicate(vector<int>& nums, int k, int t) {
        int n = nums.size();
        set<int> rec;
        for (int i = 0; i < n; i++) {
            auto iter = rec.lower_bound(max(nums[i], INT_MIN + t) - t);
            if (iter != rec.end() && *iter <= min(nums[i], INT_MAX - t) + t) {
                return true;
            }
            rec.insert(nums[i]);
            if (i >= k) {
                rec.erase(nums[i - k]);
            }
        }
        return false;
    }
};
复杂度分析

时间复杂度:O(n \log(\min(n, k)))O(nlog(min(n,k))),其中 nn 是给定数组的长度。每个元素至多被插入有序集合和从有序集合中删除一次,每次操作时间复杂度均为 O(\log(\min(n, k))O(log(min(n,k))。

空间复杂度:O(\min(n, k))O(min(n,k)),其中 nn 是给定数组的长度。有序集合中至多包含 \min(n, k + 1)min(n,k+1) 个元素。

方法二:桶
思路及算法

我们也可以使用利用桶排序的思想解决本题。我们按照元素的大小进行分桶,维护一个滑动窗口内的元素对应的元素。

对于元素 xx,其影响的区间为 [x - t, x + t][x−t,x+t]。于是我们可以设定桶的大小为 t + 1t+1。如果两个元素同属一个桶,那么这两个元素必然符合条件。如果两个元素属于相邻桶,那么我们需要校验这两个元素是否差值不超过 tt。如果两个元素既不属于同一个桶,也不属于相邻桶,那么这两个元素必然不符合条件。

具体地,我们遍历该序列,假设当前遍历到元素 xx,那么我们首先检查 xx 所属于的桶是否已经存在元素,如果存在,那么我们就找到了一对符合条件的元素,否则我们继续检查两个相邻的桶内是否存在符合条件的元素。

实现方面,我们将 \texttt{int}int 范围内的每一个整数 xx 表示为 x = (t + 1) \times a + b(0 \leq b \leq t)x=(t+1)×a+b(0≤b≤t) 的形式,这样 xx 即归属于编号为 aa 的桶。因为一个桶内至多只会有一个元素,所以我们使用哈希表实现即可。

struct HashTable {
    int key;
    int val;
    UT_hash_handle hh;
};

int getID(int x, long long w) {
    return x < 0 ? (x + 1ll) / w - 1 : x / w;
}

struct HashTable* query(struct HashTable* hashTable, int x) {
    struct HashTable* tmp;
    HASH_FIND_INT(hashTable, &x, tmp);
    return tmp;
}

bool containsNearbyAlmostDuplicate(int* nums, int numsSize, int k, int t) {
    struct HashTable* hashTable = NULL;
    for (int i = 0; i < numsSize; i++) {
        long long x = nums[i];
        int id = getID(x, t + 1ll);
        struct HashTable* tmp;
        tmp = query(hashTable, id - 1);
        if (tmp != NULL && fabs(x - tmp->val) <= t) {
            return true;
        }
        tmp = query(hashTable, id + 1);
        if (tmp != NULL && fabs(x - tmp->val) <= t) {
            return true;
        }
        tmp = query(hashTable, id);
        if (tmp != NULL) {
            return true;
        } else {
            tmp = malloc(sizeof(struct HashTable));
            tmp->key = id;
            tmp->val = x;
            HASH_ADD_INT(hashTable, key, tmp);
        }
        if (i >= k) {
            tmp = query(hashTable, getID(nums[i - k], t + 1ll));
            HASH_DEL(hashTable, tmp);
        }
    }
    return false;
}

 

 

 

Java语言本身并不包含名为"219"的内容。然而,如果你提到的是某个具体的代码段、API函数、类或者错误信息,通常我会从帮助理解和解决这类问题的角度出发给出解答。 假设你在问“Java如何处理数组中可能存在重复元素的问题”,我可以解释一下: ### Java 中处理重复元素的常见场景 #### 1. 使用 Set 集合存储数据 Set 接口不允许存储重复元素。如果你想创建一个集合只包含唯一的元素,可以使用 HashSet 类或 TreeSet 类。例如: ```java import java.util.HashSet; import java.util.Set; public class Main { public static void main(String[] args) { Set<String> set = new HashSet<>(); set.add("Element1"); set.add("Element2"); set.add("Element1"); // 这里添加相同的元素不会有任何影响 System.out.println(set); // 输出仅会显示唯一元素 } } ``` #### 2. 数据库查询结果去重 在数据库操作中,如果查询结果可能会有重复行,你可以利用 SQL 查询的 `DISTINCT` 关键字或者在 Java 代码中使用 Set 来过滤掉重复的数据。 #### 3. 数组排序并检查相邻元素是否相等 如果你需要在一个数组中找出是否有重复元素,并且按照某种顺序排序后查看相邻元素的关系,可以先对数组进行排序,然后遍历排序后的数组判断相邻元素是否相等。 ```java import java.util.Arrays; public class Main { public static void checkDuplicate(int[] arr) { Arrays.sort(arr); for (int i = 0; i < arr.length - 1; i++) { if (arr[i] == arr[i + 1]) { System.out.println("Found duplicate element: " + arr[i]); } } } public static void main(String[] args) { int[] sampleArray = {5, 3, 6, 4, 7, 8, 9}; checkDuplicate(sampleArray); } } ``` 以上就是关于Java中处理和理解数组或集合中重复元素的一些基本示例和应用场景。 --- **相关问题**: 1. **如何在Java中去除列表中的重复元素?** 2. **Java中Set与List的区别是什么?** 3. **如何高效地查找数组中的重复元素而不改变其原始顺序?**
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值