LeetCode 653. 两数之和 IV - 输入二叉搜索树
问题描述
给定一个二叉搜索树(BST)和一个目标整数 k
,判断 BST 中是否存在两个不同节点的值之和等于 k
。
示例:
输入:
5
/ \
3 6
/ \ \
2 4 7
k = 9
输出: true (因为 2 + 7 = 9)
算法思路
方法一:哈希表辅助遍历(通用解法)
- 遍历 BST:使用任意遍历方式(前序、中序、后序、层序)
- 实时检查:
- 对于每个节点值
val
,检查k - val
是否存在于哈希表 - 若存在则返回
true
;否则将val
加入哈希表
- 对于每个节点值
- 遍历结束未找到:返回
false
方法二:中序遍历+双指针(利用 BST 特性)
- 中序遍历:获得有序数组(BST 中序遍历结果为升序序列)
- 双指针查找:
- 左指针
left
指向数组开头 - 右指针
right
指向数组末尾 - 计算
nums[left] + nums[right]
:- 等于
k
:返回true
- 小于
k
:left++
- 大于
k
:right--
- 等于
- 左指针
代码实现
方法一:哈希表辅助遍历
class Solution {
public boolean findTarget(TreeNode root, int k) {
Set<Integer> seen = new HashSet<>();
return dfs(root, k, seen);
}
private boolean dfs(TreeNode node, int k, Set<Integer> seen) {
if (node == null) return false;
// 检查是否存在互补数
if (seen.contains(k - node.val)) {
return true;
}
seen.add(node.val); // 记录当前节点值
// 递归遍历左右子树
return dfs(node.left, k, seen) || dfs(node.right, k, seen);
}
}
方法二:中序遍历+双指针
class Solution {
public boolean findTarget(TreeNode root, int k) {
List<Integer> nums = new ArrayList<>();
inorder(root, nums); // 中序遍历获取有序数组
// 双指针查找两数之和
int left = 0, right = nums.size() - 1;
while (left < right) {
int sum = nums.get(left) + nums.get(right);
if (sum == k) {
return true;
} else if (sum < k) {
left++;
} else {
right--;
}
}
return false;
}
// 中序遍历:左-根-右
private void inorder(TreeNode node, List<Integer> nums) {
if (node == null) return;
inorder(node.left, nums);
nums.add(node.val);
inorder(node.right, nums);
}
}
算法分析
- 时间复杂度:
- 方法一:O(n)(每个节点访问一次)
- 方法二:O(n)(遍历 + 双指针各 O(n))
- 空间复杂度:
- 方法一:O(n)(哈希表存储节点值)
- 方法二:O(n)(存储中序遍历结果)
算法过程
BST(k=9
):
树结构:
5
/ \
3 6
/ \ \
2 4 7
方法一(哈希表):
- 遍历节点
5
:哈希表为空 → 存入5
- 遍历节点
3
:检查9-3=6
不在表 → 存入3
- 遍历节点
2
:检查9-2=7
不在表 → 存入2
- 遍历节点
4
:检查9-4=5
存在表中 → 返回true
方法二(中序遍历+双指针):
- 中序遍历结果:
[2, 3, 4, 5, 6, 7]
- 双指针操作:
left=0
(值2),right=5
(值7)→ 2+7=9 → 返回true
测试用例
public static void main(String[] args) {
Solution solution = new Solution();
// 构建示例BST
TreeNode root = new TreeNode(5);
root.left = new TreeNode(3);
root.right = new TreeNode(6);
root.left.left = new TreeNode(2);
root.left.right = new TreeNode(4);
root.right.right = new TreeNode(7);
// 测试用例1:存在解 (2+7=9)
System.out.println("Test 1: " + solution.findTarget(root, 9)); // true
// 测试用例2:存在解 (3+6=9)
System.out.println("Test 2: " + solution.findTarget(root, 9)); // true
// 测试用例3:不存在解
System.out.println("Test 3: " + solution.findTarget(root, 20)); // false
// 测试用例4:相同节点值但不同节点 (5+4=9)
System.out.println("Test 4: " + solution.findTarget(root, 9)); // true
// 测试用例5:单节点树
TreeNode single = new TreeNode(1);
System.out.println("Test 5: " + solution.findTarget(single, 1)); // false
// 测试用例6:两节点树 (1+2=3)
TreeNode twoNodes = new TreeNode(1);
twoNodes.right = new TreeNode(2);
System.out.println("Test 6: " + solution.findTarget(twoNodes, 3)); // true
}
关键点
-
方法一通用性:
- 适用于任意二叉树
- 实时检查避免完整遍历
-
方法二高效性:
- 利用 BST 有序特性
- 双指针查找效率高
-
重复值处理:
- 题目要求两个 不同节点,因此当
k=2*val
时需确保存在两个相同值节点 - 测试用例4验证此情况
- 题目要求两个 不同节点,因此当
常见问题
1. 方法一是否适用于非 BST?
是,方法一不依赖树的结构特性,适用于所有二叉树。
2. 方法二中为什么用中序遍历?
BST 的中序遍历结果是有序数组,可应用双指针技巧高效查找两数之和。
3. 如何处理 k
等于两倍节点值的情况?
需确保存在两个不同节点的值均为 k/2
。哈希表方法会自动处理(先存一个节点,遇到第二个时匹配)。
4. 最坏情况下的空间复杂度?
当树退化为链表时,递归栈深度为 O(n),哈希表/数组空间为 O(n)。