给定一个整数数组和一个整数 k,判断数组中是否存在两个不同的索引 i 和 j,使得 nums [i] = nums [j],并且 i 和 j 的差的绝对值最大为 k。
示例 1:
输入: nums = [1,2,3,1], k = 3
输出: true
示例 2:
输入: nums = [1,0,1,1], k = 1
输出: true
示例 3:
输入: nums = [1,2,3,1,2,3], k = 2
输出: false
来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/contains-duplicate-ii
方法一:初见这道题,第一想法还是遍历,也就是线性搜索方式:维护一个k大小的窗口
for (int i = 0; i < nums.length-1; i++) {
for (int j = i+1; j <= Math.min(i+k,nums.length-1); j++) {
if (nums[i] == nums[j]) {
return true;
}
}
}
return false;
时间beats:5.36%
空间beats:96.62%
这种方法用时过高,但不需要额外空间。
方法二:也有人用Set,使用k大小的滑动窗口:
Set<Integer> set = new TreeSet<>();
for (int i = 0; i < nums.length; i++) {
if (set.contains(nums[i])) {
return true;
}
set.add(nums[i]);
if (set.size() == k+1) {
set.remove(nums[i - k]);
}
}
return false;
时间:37.21%
空间:35.25%
方法一中,时间复杂度很高,此处使用TreeSet进行存储,Tree型数据结构的特点就是有序,增删查的时间复杂度都是O(logn),但实际上此处我们并不需要保持数据的有序性,于是使用方法三。
方法三:使用HashSet代替TreeSet,也就是使用散列表,增删查的时间复杂度都是O(1).
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+1) {
set.remove(nums[i - k]);
}
}
return false;
时间:74.46%
空间:45.35%、
附上常用数据结构时间复杂度:
Data Structure | 新增 | 查询 | 删除 | GetByIndex |
---|---|---|---|---|
数组 Array( T [ ]) | O(n) | O(n) | O(n) | O(1) |
链表 LinkedList | O(1) | O(n) | O(n) | O(n) |
可变数组 ArrayList | O(1) | O(n) | O(n) | O(1) |
栈Stack | O(1) | - | O(1) | - |
队列Queue | O(1) | - | O(1) | - |
HashMap<K,V> | O(1) | O(1) | O(1) | - |
TreeMap<K,V> | O(logn) | O(logn) | O(logn) | - |
HashSet | O(1) | O(1) | O(1) | - |
TreeSet | O(logn) | O(logn) | O(logn) | - |
当元素数量固定时,使用数组Array
当元素数量不固定的时候,使用List:需要在列表两端添加的时候使用LinkedList,否则使用ArrayList
需要使用键值对快速添加和查找时:如果元素没有特定顺序,使用HashMap;需要排序则使用TreeMap
当需要保存一组唯一的数据的时候:如果无排序要求,使用HashSet;有排序要求使用TreeSet