二叉搜索树的详细实现(C++)

本文详细介绍了二叉搜索树的概念,包括其特性及在搜索、插入、删除操作上的实现。二叉搜索树是一种特殊二叉树,通过中序遍历能按升序排列节点。文章提供了C++实现的二叉搜索树节点结构及插入、删除、搜索算法,并通过示例展示了如何在已有的二叉搜索树中进行这些操作。
摘要由CSDN通过智能技术生成

二叉搜索树概念

二叉搜索树(Binary Search Tree),又称为二叉查找树。

之前已经说过了二叉树的概念和实现,而二叉搜索树是一种特殊的二叉树。假设 x 为二叉树中的任意一个结点,x 结点包含包含关键字 key,结点 X 的 key 值记为 key[x],如果 y 是 x 的左子树中的一个结点,则 key[y] <= key[x];如果 y 是 x 的右子树的一个结点,则 key[y] >= key[x]。那么,这棵树就是二叉查找树,如下图所示:

二叉搜索树的详细实现-图1

在二叉搜索树中:

(01) 若任意节点的左子树不空,则左子树上所有结点的值均小于它的根结点的值。

(02) 任意节点的右子树不空,则右子树上所有结点的值均大于它的根结点的值。

(03) 任意节点的左、右子树也分别为二叉查找树。

(04) 没有键值相等的节点(no duplicate nodes)。

从上面的性质中可以看出,如果对一颗二叉树进行中序遍历,可以按从小到大的顺序,将各结点关键码排列起来,所以也称二叉搜索树为二叉排序树(Binary Sorting Tree)。

除此之外,因为二叉搜索树就是一种特殊的二叉树,其其他操作和二叉树相同,但是因为它是搜索树,就需要有自己的插入、删除、搜索算法。下面将会逐个介绍。

二叉搜索树的实现

二叉搜索树结点定义

//二叉搜索树结点类型
template<typename T>
struct BSTNode
{
    T data;    //数据域
    BSTNode<T> *left, *right;    //左子女、右子女
    BSTNode() :left(NULL), right(NULL) {}    //构造函数
    //构造函数
    BSTNode(const T d, BSTNode<T>* L = NULL, BSTNode<T>* R = NULL) :data(d), left(L), right(R) {}
};

二叉搜索树的插入

为了向二叉搜索树中插入一个新元素,必须先检查这个元素是否在树中已经存在。所以在插入之前,先使用搜索算法在树中检查要插入元素有还是没有。如果搜索成功,说明树中已经有这个元素,不再插入;如果搜索不成功,说明树中原来没有关键码等于给定值的结点,把新元素加到搜索操作停止的地方。当ptr != NULL时,它一定指向一棵子树的根,可用它所包含的关键码与给定值比较继续搜索插入位置;如果 ptr=NULL,一定是递归到空树的位置,此时将创建的新结点地址送给ptr,因为ptr是引用,新结点的地址自然送入上述某一个指针域,自动将新结点作为叶结点链入二叉搜索树中了。每次结点的插入,都要从根结点出发搜索插入位置,然后把新结点作为叶结点插入。这样不需移动结点,只需修改某个已有树中结点的一个空指针即可。

//以ptr为根的二叉搜索树中插入所含值为e1的结点
bool Insert(const T& e1, BSTNode<T>* &ptr)    //第二个参数是指针的引用
{
    if (ptr == NULL)
    {
        ptr = new BSTNode<T>(e1);    //构造新结点
        if (ptr == NULL)
        {
            cout << "Memory allocation failed!" << endl;
            exit(1);
        }
        return true;
    }
    else if (e1 < ptr->data)    //小于,插入左子树
    {
        Insert(e1, ptr->left);
    }
    else if (e1 > ptr->data)    //大于,插入右子树
    {
        Insert(e1, ptr->right);
    }
    else    //x已在树中,不插入
    {
        return false;
    }
}

二叉树的搜索

从根结点开始,沿某一个分支逐层向下进行比较判等的过程。它可以是一个递归的过程。假设想要在二叉搜索树中搜索关键码为 x 的元素,搜索过程从根结点开始。

如果根指针为 NULL,则搜索不成功。

否则用给定值 x 与根结点的关键码进行比较:

如果给定值等于根结点的关键码,则搜索成功,返回搜索成功信息,并报告搜索到的结点地址。

如果给定值小于根结点的关键码,则继续递归搜索根结点的左子树,否则,递归搜索根结点的右子树。

//在ptr为根的二叉搜索树中搜索含x的结点。若找到,返回该结点地址,否则返回NULL
BSTNode<T>* Search(T x, BSTNode<T>* ptr)
{
    if (ptr == NULL)
    {
        return NULL;
    }
    else if (x < ptr->data)
    {
        return Search(x, ptr->left);
    }
    else if (x > ptr->data)
    {
        return Search(x, ptr->right);
    }
    else
    {
        return ptr;
    }
}

二叉搜索树的删除

在二叉搜索树中删除一个结点时,必须将因删除结点而断开的二叉链表重新链接起来,同时确保二叉搜索树的性质不会失去。此外,为了保证在执行删除后,树的搜索性能不至于降低,还需要防止重新链接后树的高度不能增加。

二叉搜索树的详细实现-图2

如果想要删除叶结点,只需将其父结点指向它的指针清零,再释放它即可。

如果被删结点右子树为空,可以拿它的左子女结点顶替它的位置,再释放它。

如果被删结点左子树为空,可以拿它的右子女结点顶替它的位置,再释放它。

如果被删结点左、右子树都不空,可以在它的右子树中寻找中序下的第一个结点(关键码最小),用它的值填补到被删结点中,再来处理这个结点的删除问题,这是一个递归处理。例如,在上图中想要删除关键码为 78 的结点,它的左、右子树都不空。在它的右子树中找中序下的第一个结点,其关键码为 81。把它的值填补到被删结点中去,下面的问题就是删除关键码为 81 的结点了。这个结点左子树为空,用它的右子女(关键码为 88)代替它的位置就可以了。

//以ptr为根的二叉搜索树中删除含x的结点
bool Remove(T x, BSTNode<T>* &ptr)
{
    BSTNode<T>* temp;
    if (ptr != NULL) //ptr不为空进行操作
    {
        if (x < ptr->data)
        {
            Remove(x, ptr->left);
        }
        else if (x > ptr->data)
        {
            Remove(x, ptr->right);
        }
        //找到了要删除的结点
        //1.要删除的结点ptr同时有左右子树
        else if (ptr->left != NULL&&ptr->right != NULL)
        {
            temp = ptr->right;    //在右子树中搜索中序下的第一个结点
            while (temp->left != NULL)
            {
                temp = temp->left;
            }
            //用右子树中序下的第一个结点的值填充要删除的结点
            ptr->data = temp->data;
            //然后再新填充值ptr的右子树中删除temp的data值
            Remove(ptr->data, ptr->right);
        }
        else //不同时有左右子树
        {
            temp = ptr;        //temp记住要删除的ptr结点
            if (ptr->left == NULL) //只有右子树
            {
                ptr = ptr->right;
            }
            else    //只有左子树
            {
                ptr = ptr->left;
            }
            delete temp;    //删除结点
            temp = NULL;
            return true;
        }
    }
    else //ptr为空直接返回false
    {
        return false;
    }
}

注:其他的一些方法和二叉树是相同的,不再赘述。

详细代码实现

//二叉搜索树结点类型
template<typename T>
struct BSTNode
{
    T data;    //数据域
    BSTNode<T> *left, *right;    //左子女、右子女
    BSTNode() :left(NULL), right(NULL) {}    //构造函数
    //构造函数
    BSTNode(const T d, BSTNode<T>* L = NULL, BSTNode<T>* R = NULL) :data(d), left(L), right(R) {}
};

//二叉搜索树的定义
template <class T>
class BST
{
public:
    //普通构造函数
    BST() :root(NULL) {}
    //构造BST
    BST(T value) :root(NULL), RefValue(value)
    {
        T x;
        cin >> x;
        while (x != RefValue)
        {
            Insert(x, root);    //新建一个结点,调用Insert插入到树中
            cin >> x;
        }
    }
    //析构
    ~BST() { Destroy(root); }
    //插入
    bool Insert(T x) { return Insert(x, root); }
    //删除
    bool Remove(T x) { return Remove(x, root); }
    //搜索
    bool Search(T x) { return (Search(x, root) != NULL) ? true : false; }
    //中序遍历
    void InOrder() { InOrder(root); }

protected:

    //以ptr为根的二叉搜索树中插入所含值为e1的结点
    bool Insert(const T& e1, BSTNode<T>* &ptr)    //第二个参数是指针的引用
    {
        if (ptr == NULL)
        {
            ptr = new BSTNode<T>(e1);    //构造新结点
            if (ptr == NULL)
            {
                cout << "Memory allocation failed!" << endl;
                exit(1);
            }
            return true;
        }
        else if (e1 < ptr->data)    //小于,插入左子树
        {
            Insert(e1, ptr->left);
        }
        else if (e1 > ptr->data)    //大于,插入右子树
        {
            Insert(e1, ptr->right);
        }
        else    //x已在树中,不插入
        {
            return false;
        }
    }

    //以ptr为根的二叉搜索树中删除含x的结点
    bool Remove(T x, BSTNode<T>* &ptr)
    {
        BSTNode<T>* temp;
        if (ptr != NULL) //ptr不为空进行操作
        {
            if (x < ptr->data)
            {
                Remove(x, ptr->left);
            }
            else if (x > ptr->data)
            {
                Remove(x, ptr->right);
            }
            //找到了要删除的结点
            //1.要删除的结点ptr同时有左右子树
            else if (ptr->left != NULL&&ptr->right != NULL)
            {
                temp = ptr->right;    //在右子树中搜索中序下的第一个结点
                while (temp->left != NULL)
                {
                    temp = temp->left;
                }
                //用右子树中序下的第一个结点的值填充要删除的结点
                ptr->data = temp->data;
                //然后再新填充值ptr的右子树中删除temp的data值
                Remove(ptr->data, ptr->right);
            }
            else //不同时有左右子树
            {
                temp = ptr;        //temp记住要删除的ptr结点
                if (ptr->left == NULL) //只有右子树
                {
                    ptr = ptr->right;
                }
                else    //只有左子树
                {
                    ptr = ptr->left;
                }
                delete temp;    //删除结点
                temp = NULL;
                return true;
            }
        }
        else //ptr为空直接返回false
        {
            return false;
        }
    }

    //在ptr为根的二叉搜索树中搜索含x的结点。若找到,返回该结点地址,否则返回NULL
    BSTNode<T>* Search(T x, BSTNode<T>* ptr)
    {
        if (ptr == NULL)
        {
            return NULL;
        }
        else if (x < ptr->data)
        {
            return Search(x, ptr->left);
        }
        else if (x > ptr->data)
        {
            return Search(x, ptr->right);
        }
        else
        {
            return ptr;
        }
    }

    //中序遍历
    void InOrder(BSTNode<T>* root)
    {
        if (root != NULL)
        {
            InOrder(root->left);
            cout << root->data << " ";
            InOrder(root->right);
        }
    }

    //销毁以root为根的二叉树搜索树函数
    void Destroy(BSTNode<T>* &root)
    {
        if (root == NULL)
        {
            return;
        }
        if (root->left != NULL)
        {
            Destroy(root->left);
        }
        if (root->right != NULL)
        {
            Destroy(root->right);
        }
        delete root;
        root = NULL;
    }
private:
    BSTNode<T>* root;    //根指针
    T RefValue;    //输入结束标识
};

int main(int argc, char* argv[])
{
    //g a e d f h j i l k #
    BST<char> tree('#');
    tree.InOrder();
    cout << endl;
    cout << tree.Search('e') << endl;
    cout << tree.Insert('z') << endl;
    tree.InOrder();
    cout << endl;
    cout << tree.Remove('z') << endl;
    cout << tree.Remove('j') << endl;
    tree.InOrder();
    cout << endl;
    return 0;
}
  • 4
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论
二叉搜索树的删除操作可以分为以下几步: 1. 首先找到要删除的节点,可以使用二叉搜索树的查找方法,从根节点开始递归查找。 2. 如果要删除的节点是叶子节点,直接删除即可。 3. 如果要删除的节点只有一个子节点,将该子节点接替要删除的节点即可。 4. 如果要删除的节点有两个子节点,需要找到要删除节点的中序遍历后继节点(也就是比要删除的节点大的最小节点),将其值复制到要删除的节点中,然后再将中序遍历后继节点删除。 以下是一个示例代码实现: ```python class TreeNode: def __init__(self, val=0, left=None, right=None): self.val = val self.left = left self.right = right class Solution: def deleteNode(self, root: TreeNode, key: int) -> TreeNode: if not root: return None # 找到要删除的节点 if key < root.val: root.left = self.deleteNode(root.left, key) elif key > root.val: root.right = self.deleteNode(root.right, key) else: # 叶子节点或只有一个子节点 if not root.left: return root.right elif not root.right: return root.left # 有两个子节点 else: successor = root.right while successor.left: successor = successor.left root.val = successor.val root.right = self.deleteNode(root.right, successor.val) return root ``` 在这个实现中,我们首先使用递归找到要删除的节点,然后根据节点的情况进行删除操作。如果节点是叶子节点或只有一个子节点,直接将其删除或将其子节点接到父节点上。如果节点有两个子节点,找到中序遍历后继节点,将其值复制到要删除的节点上,然后再将中序遍历后继节点删除。最后返回删除操作后的根节点即可。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

code_peak

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值