携手创作,共同成长!这是我参与「掘金日新计划 · 8 月更文挑战」的第12天,点击查看活动详情
二叉搜索树的定义
二叉查找树(Binary Search Tree),(又:二叉搜索树,二叉排序树)它或者是一棵空树,或者是具有下列性质的二叉树: 若它的左子树不空,则左子树上所有结点的值均小于它的根结点的值; 若它的右子树不空,则右子树上所有结点的值均大于它的根结点的值; 它的左、右子树也分别为二叉排序树。二叉搜索树作为一种经典的数据结构,它既有链表的快速插入与删除操作的特点,又有数组快速查找的优势;所以应用十分广泛,例如在文件系统和数据库系统一般会采用这种数据结构进行高效率的排序与检索操作。
二叉搜索树主要支持三个操作
搜索
关于二叉搜索树的搜索,对于每个节点:
- 如果目标值等于节点的值,则返回节点;
- 如果目标值小于节点的值,则继续在左子树中搜索;
- 如果目标值大于节点的值,则继续在右子树中搜索。
插入
插入到操作与搜索类似,对于每个节点:
- 根据节点值与目标节点值的关系,搜索左子树或右子树;
- 重复上一步直到到达外部节点;
- 根据节点的值与目标节点的值的关系,将新节点添加为其左侧或右侧的子节点。
删除
删除的操作比起前两个操作会稍微复杂些,需要注意的点如下:
- 如果目标节点
没有子节点
,我们可以直接移除
该目标节点。 - 如果目标节
只有一个子节点
,我们可以用其子节点作为替换
。 - 如果目标节点
有两个子节点
,我们需要用其中序后继节点或者前驱节点
来替换,再删除该目标节点。
题目
给你一个整数数组 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
输入: nums = [1,5,9,1,5,9], k = 2, t = 3
输出: false
提示
0 <= nums.length <= 2 * 10^4
-2^31 <= nums[i] <= 2^31 - 1
0 <= k <= 10^4
0 <= t <= 2^31 - 1
代码实现
题目中有两个要求:
- 两个不同下标 的绝对值要小于等于
k
; - 两个不同下标位置的元素差的绝对值要小于等于
t
。
要求2
我们需要每次都进行比较判断,至于要求1
,我们可以维护一个长度为k+1
的滑动窗口来缓存一些有用的信息。
频繁的删除和添加元素最合适的数据结构是[查找表]
,[查找表]
的实现我们可以使用[哈希表]
或者[二叉搜索树]
来实现,结合上面的滑动窗口,我们在这里采用[二叉搜索树]
来实现该题。
解题步骤:
- 使用
TreeSet
实现查找表
的功能 - 一边添加,一边查询看是否有大于等于
要求2
的值 - 当表长度达到
k + 1
后,后续加入新的元素则需删除最左边的元素
public class Solution {
public boolean containsNearbyAlmostDuplicate(int[] nums, int k, int t) {
// 滑动窗口结合查找表,此时滑动窗口即为查找表本身(控制查找表的大小即可控制窗口大小)
TreeSet<Long> set = new TreeSet<>();
for (int i = 0; i < nums.length; i++) {
// 边添加边查找,查找表中是否有大于等于 nums[i] - t 且小于等于 nums[i] + t 的值
Long ceiling = set.ceiling((long) nums[i] - (long) t);
if (ceiling != null && ceiling <= ((long) nums[i] + (long) t)) {
return true;
}
// 添加后,控制查找表(窗口)大小,移除窗口最左边元素
set.add((long) nums[i]);
if (set.size() == k + 1) {
set.remove((long) nums[i - k]);
}
}
return false;
}
}