LeetCode-450. 删除二叉搜索树中的节点
题目描述:
给定一个二叉搜索树的根节点 root 和一个值 key,删除二叉搜索树中的 key 对应的节点,并保证二叉搜索树的性质不变。返回二叉搜索树(有可能被更新)的根节点的引用。
一般来说,删除节点可分为两个步骤:
首先找到需要删除的节点;
如果找到了,删除它。
关于二叉搜索树,在本文下面有相关的介绍,只要是满足左子树节点小于根节点,右子树节点大于根节点的一颗二叉树就是二叉搜索树(BST)。
解题思路:
具体来说应该有三种思路
1、递归法
2、迭代法(本题采用)
3、通用二叉树覆盖删除法
迭代法删除二叉搜索树节点代码
/**
* Definition for a binary tree node.
* struct TreeNode {
* int val;
* TreeNode *left;
* TreeNode *right;
* TreeNode() : val(0), left(nullptr), right(nullptr) {}
* TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}
* TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {}
* };
*/
class Solution {
public:
TreeNode* deleteNode(TreeNode* root, int key) {
TreeNode *cur = root;
TreeNode * parent = NULL;
TreeNode * right;
while(cur){
if(cur->val == key){//发现要删除的节点
TreeNode* aftern = NULL;
if(cur->left && cur->right){//要删除节点具有左子树和右子树,转移左子树的右子树到右子树
right = cur->right;//要删除节点的右根节点
while(right && right->left != NULL){
right = right->left;
}
right->left = cur->left->right;//转移要删除节点右子树
cur->left->right = cur->right;
aftern = cur->left;
}
else if(cur->left){
aftern = cur->left;
}else if(cur->right){
aftern = cur->right;
}
if(!parent){//删除的是根节点
root = aftern;
break;
}else{//删除的非根节点
if(key < parent->val){
parent->left = aftern;
}else{
parent->right = aftern;
}
break;
}
}
parent = cur;
if(key < cur->val){
cur = cur->left;
}else{
cur = cur->right;
}
}
return root;
}
};
二叉搜索树(BST,Binary Search Tree)
也称二叉排序树或二叉查找树。
二叉搜索树:一棵二叉树,可以为空;如果不为空,满足以下性质:
非空左子树的所有键值小于其根结点的键值。
非空右子树的所有键值大于其根结点的键值。
左、右子树都是二叉搜索树。
二叉搜索树的例子
二叉搜索树的查找操作:
(1)查找从根结点开始,如果树为空,返回NULL
(2)若搜索树非空,则根结点关键字和X进行比较,并进行不同处理:
① 若X小于根结点键值,只需在左子树中继续搜索;
② 如果X大于根结点的键值,在右子树中进行继续搜索;
③若两者比较结果是相等,搜索完成,返回指向此结点的指针。
代码如下:
Position Find( ElementType X, BinTree BST )
{
if( !BST ) return NULL; /*查找失败*/
if( X > BST->Data )
return Find( X, BST->Right ); /*在右子树中继续查找*/
else if( X < BST->Data )
return Find( X, BST->Left ); /*在左子树中继续查找*/
else /* X == BST->Data */
return BST; /*查找成功,返回结点的找到结点的地址*/
}
值得说明的是,上述的代码使用的是递归的方式,而使用递归会导致效率不高。恰巧这段代码又是尾递归的方式(尾递归就是程序分支的最后,也就是最后要返回的时候才出现递归),从编译的角度来讲,尾递归都可以用循环的方式去实现。由于非递归函数的执行效率高,可将“尾递归”函数改为迭代函数。
迭代法代码如下:
Position IterFind( ElementType X, BinTree BST )
{
while( BST )
{
if( X > BST->Data )
BST = BST->Right; /*向右子树中移动,继续查找*/
else if( X < BST->Data )
BST = BST->Left; /*向左子树中移动,继续查找*/
else /* X == BST->Data */
return BST; /*查找成功,返回结点的找到结点的地址*/
}
return NULL; /*查找失败*/
}
最后要说的是,查找的效率决定于树的高度。
2. 查找最大和最小元素
首先说明的是:
①最大元素一定是在树的最右分枝的端结点上。
②最小元素一定是在树的最左分枝的端结点上。
根据上述两点,我们可以很轻松的把代码写出来:
Position FindMin( BinTree BST )
{
if( !BST )
return NULL; /*空的二叉搜索树,返回NULL*/
else if( !BST->Left )
return BST; /*找到最左叶结点并返回*/
else
return FindMin( BST->Left ); /*沿左分支继续查找*/
}
上面是查找最小元素的递归函数,由于这也是个尾递归,所以我们依旧可以把它改成迭代函数,代码如下:
Position FindMax( BinTree BST )
{
if(BST )
while( BST->Right ) /*沿右分支继续查找,直到最右叶结点*/
BST = BST->Right;
return BST;
}
- 二叉搜索树的插入
二叉搜索树在插入前,肯定要找到插入的位置。所以解决这个问题的关键就是要找到元素应该插入的位置。 - 二叉搜索树的删除
相对插入而言比较麻烦,需要重构一下二叉搜索树,具体看最上方代码。