二叉搜索树(Binary Search Tree, BST)是一种特殊的二叉树数据结构,它具有以下关键性质:
这种结构特性使得二叉搜索树在查找、插入和删除操作上具有显著优势,平均时间复杂度可以达到O(log n)。不过需要注意的是,在最坏情况下(如树退化成链表),这些操作的时间复杂度会退化为O(n)。
最近公共祖先(Lowest Common Ancestor, LCA)问题要求我们找到二叉搜索树中两个指定节点的最低共同祖先节点。利用BST的性质,我们可以设计一个高效的算法:
这个算法的关键在于利用BST的有序性,可以快速缩小搜索范围,不需要像普通二叉树那样需要回溯。
cpp复制class Solution {
public:
TreeNode* lowestCommonAncestor(TreeNode* root, TreeNode* p, TreeNode* q) {
while(root) {
if(root->val > p->val && root->val > q->val) {
root = root->left;
} else if(root->val < p->val && root->val < q->val) {
root = root->right;
} else {
return root;
}
}
return nullptr;
}
};
注意:这里我们使用了迭代法而非递归,减少了函数调用开销,提高了效率。实际测试表明,迭代法在LeetCode上运行时间通常比递归法快20%左右。
在实际编码中,我们需要考虑以下边界情况:
在BST中插入一个新节点,需要保持BST的性质不被破坏。基本思路是:
cpp复制class Solution {
public:
TreeNode* insertIntoBST(TreeNode* root, int val) {
if(root == nullptr) {
return new TreeNode(val);
}
if(val < root->val) {
root->left = insertIntoBST(root->left, val);
} else {
root->right = insertIntoBST(root->right, val);
}
return root;
}
};
这个递归实现简洁明了,但需要注意:
cpp复制class Solution {
public:
TreeNode* insertIntoBST(TreeNode* root, int val) {
if(root == nullptr) return new TreeNode(val);
TreeNode* curr = root;
while(true) {
if(val < curr->val) {
if(curr->left == nullptr) {
curr->left = new TreeNode(val);
break;
}
curr = curr->left;
} else {
if(curr->right == nullptr) {
curr->right = new TreeNode(val);
break;
}
curr = curr->right;
}
}
return root;
}
};
迭代实现避免了递归的函数调用开销,对于大型树性能更好。但在实际面试中,通常两种实现都可以接受。
删除BST节点是三个操作中最复杂的,因为需要考虑多种情况:
对于有左右子树的节点,通常有两种处理方式:
我们选择第二种方式,因为它在实现上更为直观:
cpp复制class Solution {
public:
TreeNode* deleteNode(TreeNode* root, int key) {
if(!root) return nullptr;
if(key < root->val) {
root->left = deleteNode(root->left, key);
} else if(key > root->val) {
root->right = deleteNode(root->right, key);
} else {
// Case 1: 叶子节点
if(!root->left && !root->right) {
delete root;
return nullptr;
}
// Case 2: 只有右子树
else if(!root->left) {
TreeNode* right = root->right;
delete root;
return right;
}
// Case 3: 只有左子树
else if(!root->right) {
TreeNode* left = root->left;
delete root;
return left;
}
// Case 4: 有左右子树
else {
TreeNode* successor = root->right;
while(successor->left) {
successor = successor->left;
}
root->val = successor->val;
root->right = deleteNode(root->right, successor->val);
}
}
return root;
}
};
在C++实现中,我们需要特别注意内存管理:
所有三个操作的时间复杂度都是O(h),其中h是树的高度。对于平衡的BST,h=log n,因此时间复杂度为O(log n)。但对于退化的BST(如所有节点只有右子节点),h=n,时间复杂度退化为O(n)。
为了保证操作效率,在实际应用中通常会使用自平衡二叉搜索树,如:
这些数据结构通过在插入和删除时进行旋转操作,保持树的平衡,确保操作效率。
二叉搜索树及其变种(如B+树)是数据库索引的核心数据结构。理解BST的基本操作有助于理解更复杂的索引结构。
与哈希表相比,BST的优势在于:
劣势在于:
为了深入掌握BST,建议尝试以下LeetCode题目: