滑动窗口10:存在重复元素的三个题
Posted 纵横千里,捭阖四方
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了滑动窗口10:存在重复元素的三个题相关的知识,希望对你有一定的参考价值。
滑动窗口的问题还有很多,我们这里先看前面的几道,本文我们分析一个专题:存在重复元素的三道题。对应LeetCode的217,219和220三道题。在做了这三个题之后感觉有点无聊,但是吧,面试算法本来就无聊,还是一起做一做吧。
LeetCode217题 给定一个整数数组,判断是否存在重复元素。如果存在一值在数组中出现至少两次,函数返回 true
。如果数组中每个元素都不相同,则返回 false
。
Leetcode219题,给定一个整数数组和一个整数 k,判断数组中是否存在两个不同的索引 i 和 j,使得 nums [i] = nums [j],并且 i 和 j 的差的 绝对值 至多为 k。
LeetCode220 给你一个整数数组 nums 和两个整数 k 和 t 。请你判断是否存在 两个不同下标 i 和 j,使得 abs(nums[i] - nums[j]) <= t ,同时又满足 abs(i - j) <= k 。如果存在则返回 true,不存在返回 false。
这三个题整体难度不大,前面两个是简单题,最后一个是中等题,我们现在就一起看一下该怎么解决。
1.LeetCode217,给定一个整数数组,判断是否存在重复元素。
如果存在一值在数组中出现至少两次,函数返回 true
。如果数组中每个元素都不相同,则返回 false
。
本题不难,一个方法是先排序,在对数字从小到大排序之后,数组的重复元素一定出现在相邻位置中。因此,我们可以扫描已排序的数组,每次判断相邻的两个元素是否相等,如果相等则说明存在重复的元素。
class Solution {
public boolean containsDuplicate(int[] nums) {
Arrays.sort(nums);
int n = nums.length;
for (int i = 0; i < n - 1; i++) {
if (nums[i] == nums[i + 1]) {
return true;
}
}
return false;
}
}
排序虽然能解决问题,但是代价是比较高的,我们可以使用Hash来解决,对于数组中每个元素,我们将它插入到哈希表中。如果插入一个元素时发现该元素已经存在于哈希表中,则说明存在重复的元素。
class Solution {
public boolean containsDuplicate(int[] nums) {
Set<Integer> set = new HashSet<Integer>();
for (int x : nums) {
if (!set.add(x)) {
return true;
}
}
return false;
}
}
2. 存在重复元素,无聊的题
给定一个整数数组和一个整数 k,判断数组中是否存在两个不同的索引 i 和 j,使得 nums [i] = nums [j],并且 i 和 j 的差的 绝对值至多为 k。
示例1:
输入: nums = [1,2,3,1], k = 3
输出: true
示例2:
输入: nums = [1,2,3,1,2,3], k = 2
输出: false
这个题需要维护一个k大小的滑动窗口,用散列表来维护这个k大小的滑动窗口就行了。
(1) 遍历数组,对于每个元素做以下操作:
- 在散列表中搜索当前元素,如果找到了就返回 true。
- 在散列表中插入当前元素。
- 如果当前散列表的大小超过了 kk, 删除散列表中最旧的元素。
(2) 返回 false。
public boolean containsNearbyDuplicate(int[] nums, int k) {
Set<Integer> set = new HashSet<>();
for (int i = 0; i < nums.length; ++i) {
if (set.contains(nums[i])) return true;
set.add(nums[i]);
if (set.size() > k) {
set.remove(nums[i - k]);
}
}
return false;
}
# 3.存在重复元素III
给你一个整数数组 nums 和两个整数 k 和 t 。请你判断是否存在 两个不同下标 i 和 j,使得 abs(nums[i] - nums[j]) <= t ,同时又满足 abs(i - j) <= k 。
如果存在则返回 true,不存在返回 false。
示例1;
输入:nums = [1,2,3,1], k = 3, t = 0
输出:true
示例2:
输入:nums = [1,5,9,1,5,9], k = 2, t = 3
输出:false
用滑动窗口+暴力遍历解法会超时,所以更换TreeSet数据结构。
使用TreeSet维护一个长度小于等于k的滑动窗口。对于一个新加入的元素,我们需要比较滑动窗口中的每一个元素是否都满足题意,即abs(nums[i] - nums[j]) <= t,但若想避开遍历,就要利用好TreeSet的特性,以及它封装的神器函数:ceiling()
- ceiling()方法用于返回大于或等于給定元素的该集合中的最小元素;如果没有此元素,則返回null。
- 我们要验证集合中是否有元素位于[myNum[i]-t,myNum[i]+t]区间内,则使用 Long ceiling = set.ceiling(myNum[i]-t);来获取大于等于myNum[i]-t的最小元素,若此元素也小于等于myNum[i]+t,则找到了这样一个元素位于[myNum[i]-t,myNum[i]+t]区间内。
注意维护滑动窗口时的细节
class Solution {
public boolean containsNearbyAlmostDuplicate(int[] nums, int k, int t) {
int n = nums.length;
if(n<=1) return false;
long[] myNum = new long[n];
for(int i = 0;i<n;i++){
myNum[i] = nums[i];
}
TreeSet<Long> set = new TreeSet<>();
for(int i = 0;i<n;i++){
Long ceiling = set.ceiling(myNum[i]-t);
if(ceiling!=null && ceiling<=myNum[i]+t){
return true;
}
set.add(myNum[i]);
if(set.size()>k){
set.remove(myNum[i-k]);
}
}
return false;
}
}
双指针和滑动窗口主题暂时到这里,接下来我们来研究分治的问题。
以上是关于滑动窗口10:存在重复元素的三个题的主要内容,如果未能解决你的问题,请参考以下文章
281.LeetCode | 220. 存在重复元素 III