二叉搜索树(Binary Search Tree, BST)是一种特殊的二叉树数据结构,它满足以下性质:
验证一棵二叉树是否是BST看似简单,但实际实现中有几个关键点需要注意:
中序遍历(In-order Traversal)是指按照"左-根-右"的顺序遍历二叉树。对于BST而言,中序遍历的结果应该是一个严格递增的序列。这是验证BST的核心理论基础。
注意:严格递增意味着不能有相等的值,这是BST定义的一部分。如果题目允许相等值,需要根据具体要求调整验证逻辑。
javascript复制var isValidBST = function(root) {
const arr = [];
function dfs(root) {
if(!root) return;
dfs(root.left);
arr.push(root.val);
dfs(root.right);
}
dfs(root);
for(let i = 1; i < arr.length; i++) {
if(arr[i] <= arr[i-1]) return false;
}
return true;
};
在实际编码中,有几个边界条件需要特别注意:
javascript复制var isValidBST = function(root) {
let prev = -Infinity;
let ans = true;
function dfs(root) {
if(!root || !ans) return;
dfs(root.left);
if(root.val <= prev) {
ans = false;
return;
}
prev = root.val;
dfs(root.right);
}
dfs(root);
return ans;
};
| 方案 | 时间复杂度 | 空间复杂度 | 提前终止 |
|---|---|---|---|
| 基础版 | O(n) | O(n) | 否 |
| 优化版 | O(n) | O(h) | 是 |
对于追求极致空间优化的场景,可以考虑Morris中序遍历,它能实现O(1)空间复杂度(不考虑递归栈):
javascript复制var isValidBST = function(root) {
let prev = -Infinity;
let curr = root;
while (curr) {
if (!curr.left) {
if (curr.val <= prev) return false;
prev = curr.val;
curr = curr.right;
} else {
let pred = curr.left;
while (pred.right && pred.right !== curr) {
pred = pred.right;
}
if (!pred.right) {
pred.right = curr;
curr = curr.left;
} else {
pred.right = null;
if (curr.val <= prev) return false;
prev = curr.val;
curr = curr.right;
}
}
}
return true;
};
提示:Morris遍历虽然空间效率高,但实现复杂且会临时修改树结构,在面试或实际工程中需谨慎使用。
仅检查直接子节点:
javascript复制// 错误实现示例
function isValidBST(root) {
if (!root) return true;
if (root.left && root.left.val >= root.val) return false;
if (root.right && root.right.val <= root.val) return false;
return isValidBST(root.left) && isValidBST(root.right);
}
这种实现无法保证整个子树都满足条件。
边界值处理不当:
递归终止条件错误:
全面的测试用例应包含:
示例测试用例:
javascript复制// 测试用例示例
const testCases = [
{ input: null, expected: true }, // 空树
{ input: new TreeNode(1), expected: true }, // 单节点
{ input: createTree([2,1,3]), expected: true }, // 标准BST
{ input: createTree([5,1,4,null,null,3,6]), expected: false }, // 非BST
{ input: createTree([1,1]), expected: false }, // 重复值
{ input: createTree([Number.MAX_SAFE_INTEGER]), expected: true }, // 边界值
];
可视化遍历过程:
javascript复制function dfs(root) {
if(!root) return;
console.log(`进入节点 ${root ? root.val : 'null'}`);
dfs(root.left);
console.log(`访问节点 ${root.val}`);
dfs(root.right);
console.log(`离开节点 ${root.val}`);
}
追踪prev值变化:
javascript复制console.log(`当前值: ${root.val}, 前驱值: ${prev}`);
使用Chrome调试器:
如果题目允许左子树包含等于当前节点的值,只需修改比较条件:
javascript复制if (root.val < prev) { // 原为 <=
ans = false;
return;
}
递归实现简洁但可能栈溢出,迭代实现使用显式栈:
javascript复制var isValidBST = function(root) {
const stack = [];
let prev = -Infinity;
while (stack.length || root) {
while (root) {
stack.push(root);
root = root.left;
}
root = stack.pop();
if (root.val <= prev) return false;
prev = root.val;
root = root.right;
}
return true;
};
另一种思路是传递允许的数值范围:
javascript复制var isValidBST = function(root) {
function helper(node, lower, upper) {
if (!node) return true;
if (node.val <= lower || node.val >= upper) return false;
return helper(node.left, lower, node.val) &&
helper(node.right, node.val, upper);
}
return helper(root, -Infinity, Infinity);
};
这种方法在特定场景下可能更直观,但递归深度与树高相关。
函数式与纯函数:
尾递归优化:
TypeScript增强:
typescript复制interface TreeNode {
val: number;
left: TreeNode | null;
right: TreeNode | null;
}
function isValidBST(root: TreeNode | null): boolean {
// 实现...
}
性能监控:
javascript复制console.time('isValidBST');
isValidBST(largeTree);
console.timeEnd('isValidBST');
BST验证算法虽然简单,但其思想在实际工程中有广泛应用:
理解BST验证的核心思想后,可以将其推广到更复杂的树结构验证场景中。