鉴于提供了二叉搜索树,有效的方法是遍历树并比较根节点与目标的绝对差(距离),同时跟踪“距离”较小的节点以更新@987654322 @value 当我们遇到更接近目标值的节点时。接下来,我们可以开始将当前节点的值与目标进行比较,如果它小于目标我们要在右子树中搜索大于或等于根节点的值,如果当前节点值大于我们要在左子树中搜索严格小于根节点的值的目标。
这样做,我们可以在每一步(平均)消除一半的 BST,这意味着我们遍历左子树(消除右半部分)或遍历右子树(消除左半部分),而跟踪最近的节点并在我们找到更接近目标的节点时更新它。
对于你提供的5 4 9的BST,确实满足BST的要求:
- 根节点左侧的所有值都严格小于根节点
- 根节点右边的所有值都大于等于根节点
- 每个父节点最多只能有两个子节点
5
/ \
4 9
对于上下文,BST 中的节点将具有结构:
struct Node {
int data;
Node *left;
Node *right;
Node() { data = 0; left = right = nullptr; };
Node(int val) { data = val; left = right = nullptr; };
}
以下是一些 C++ 解决方案,但这些逻辑可以很容易地与 Java 语法一起使用。
递归方法平均在 O(log(n)) 时间和 O(log(n)) 空间执行,因为我们递归调用 minDiffHelper 并将这些调用或“帧”添加到占用的调用堆栈中空间。
// On average: O(log(n)) time and O(log(n)) space
// Worst case: O(n) time and O(n) space
// where n = number of nodes in the tree
int minDiffHelper(Node *root, int target, int closest);
int minDiff(Node *root, int target) {
return minDiffHelper(root, K, root->data);
}
int minDiffHelper(Node *root, int target, int closest) {
if (abs(target-closest) > abs(target-root->data)) {
closest = root->data;
}
if (root->left != nullptr && root->data > target) {
return minDiffHelper(root->left, target, closest);
} else if (root->right != nullptr && root->data < target) {
return minDiffHelper(root->right, target, closest);
} else {
return closest;
}
}
迭代方法也平均在 O(log(n)) 时间执行,并且不向调用堆栈添加任何递归调用,因此我们只消耗恒定的 O(1) 空间而不是 O(log(n) 空间当我们递归添加到调用堆栈时,我们会看到。
两种算法(递归和迭代)在最坏情况下都有 O(n) 空间,或者可以写成 O(d) 空间,其中 d = 树的深度。
// On average: O(log(n)) time and O(1) space
// Worst case: O(n) time and O(n) space
// where n = number of nodes in the tree
int minDiffHelper(Node *root, int target, int closest);
int minDiff(Node *root, int target) {
return minDiffHelper(root, target, root->data);
}
int minDiffHelper(Node *root, int target, int closest) {
Node *current = root;
while (current != nullptr) {
if (abs(target-closest) > abs(target-current->data)) {
closest = current->data;
}
if (current->left != nullptr && current->data > target) {
current = current->left;
} else if (current->right != nullptr && current->data < target) {
current = current->right;
} else break;
}
return closest;
}
GeeksForGeeks 有一个很好的practice problem 来测试你的理解。它要求一个稍微不同的解决方案(返回最近元素到给定目标的距离),但这可以通过返回距离abs(target-closest) 而不是简单地返回节点值closest 来轻松处理。