09 二叉搜索树

09 二叉搜索树


作者:abnb250/山中鹤鸣
博客:https://www.cnblogs.com/abnb250
交流群:1062464078
点击返回目录



前情提要

这一节用模板实现 BST,递归完成插入、删除、查找、四种遍历,并完整实现拷贝/移动语义、height、clear,以及一个 cloneRec 的异常安全克隆。
注释版集中在删除节点、克隆异常、移动语义三处:
删除叶子节点时不能 return n(已 delete),删除单孩子节点时必须先保存孩子再 delete n,双孩子删除的 dummy 参数只接收递归里的 erased;
cloneRec 的 catch 里不能只 delete tmp,左子树可能已克隆,必须 clearRec(tmp);
移动构造/移动赋值必须把源的 root_ 置空,否则源析构会二次释放整棵树。



代码(加注释版)

注释解释了两种场景:

  1. 代码有反直觉痕迹,自然写法就在旁边。
  2. 代码看起来自然,但背后有隐形决策。

BST.h

#ifndef BST_H
#define BST_H

#include <iostream>
#include <stdexcept>
#include <algorithm>
#include <utility>  
#include <cstddef>
#include <queue>


template<class T>
class BST{
private:
    struct Node{
        T v;
        Node* left = nullptr;
        Node* right = nullptr;
        explicit Node(const T& val) : v(val) {}
        explicit Node(T&& val) : v(std::move(val)) {}
    };
    Node* root_ = nullptr;
    std::size_t size_ = 0;

    void clearRec(Node* n) noexcept;

    Node* minNode(Node* n) const;
    Node* maxNode(Node* n) const;

    Node* insertRec(Node* n, const T& val, bool& inserted);
    Node* insertRec(Node* n, T&& val, bool& inserted);

    Node* eraseRec(Node* n, const T& val, bool& erased);

    Node* findNode(const T& val) const;

    std::size_t heightRec(const Node* n) const;
    Node* cloneRec(const Node* n);

    template<class Fn> void inorderRec(Node* n, Fn& fn) const;
    template<class Fn> void preorderRec(Node* n, Fn& fn) const;
    template<class Fn> void postorderRec(Node* n, Fn& fn) const;

public:
    BST() noexcept = default;
    ~BST() noexcept;

    BST(const BST& other);
    BST& operator=(const BST& other);
    BST(BST&& other) noexcept;
    BST& operator=(BST&& other) noexcept;

    void swap(BST& other) noexcept;
    friend void swap(BST& a, BST& b) noexcept {a.swap(b);}

    bool insert(const T& val);
    bool insert(T&& val);

    bool contains(const T& val) const;
    const T* find(const T& val) const;

    const T& minimum() const;
    const T& maximum() const;

    bool erase(const T& val);

    template<class Fn> void inorder(Fn&& fn) const;
    template<class Fn> void preorder(Fn&& fn) const;
    template<class Fn> void postorder(Fn&& fn) const;
    template<class Fn> void levelorder(Fn&& fn) const;

    std::size_t size() const noexcept {return size_;}
    bool empty() const noexcept {return size_ == 0;}
    std::size_t height() const;
    void clear() noexcept;
};

template <typename T>
void BST<T>::clearRec(Node *n) noexcept
{
    if (n == nullptr)
        return;
    if (n->left)
        clearRec(n->left);
    if (n->right)
        clearRec(n->right);
    delete n;
}

template <typename T>
typename BST<T>::Node* BST<T>::minNode(Node *n) const
{
    if (n == nullptr)
    {
        throw std::out_of_range("nullptr pointer");
    }
    if (n->left)
        return minNode(n->left);
    return n;
}
template <typename T>
typename BST<T>::Node* BST<T>::maxNode(Node *n) const
{
    if (n == nullptr)
    {
        throw std::out_of_range("nullptr pointer");
    }
    if (n->right)
        return maxNode(n->right);
    return n;
}

template <typename T>
typename BST<T>::Node* BST<T>::insertRec(Node *n, const T &val, bool &inserted)
{
    if (n == nullptr)
    {
        inserted = true;
        n = new Node(val);
        return n;
    }

    if (val > n->v)
        n->right = insertRec(n->right, val, inserted);
    else if (val < n->v)
        n->left = insertRec(n->left, val, inserted);
    if (val == n->v) return n;  
    return n;
}

template <typename T>
typename BST<T>::Node* BST<T>::insertRec(Node *n, T&& val, bool &inserted)
{
    if (n == nullptr)
    {
        inserted = true;
        return new Node(std::move(val));
    }

    if (val < n->v)
        n->left = insertRec(n->left, std::move(val), inserted);
    else if (val > n->v)
        n->right = insertRec(n->right, std::move(val), inserted);
    return n;
}

template <typename T>
typename BST<T>::Node* BST<T>::eraseRec(Node *n, const T &val, bool &erased)
{
    if (n == nullptr)
        return nullptr;

    if (val < n->v)
    {
        n->left = eraseRec(n->left, val, erased);
        return n;
    }
    else if (val > n->v)
    {
        n->right = eraseRec(n->right, val, erased);
        return n;
    }

    erased = true;

    if (!n->left && !n->right)
    {
        // 不能 return n,n 已 delete,父节点会挂悬空指针
        delete n;
        return nullptr;
    }

    if (!(n->left))
    {
        // 不能直接 delete n; return n->right;,delete 后访问 n->right 是 UB;必须先保存孩子
        Node* tmp = n->right;
        delete n;
        return tmp;
    }
    if (!(n->right))
    {
        // 同上,单孩子删除必须先保存孩子再 delete n
        Node* tmp = n->left;
        delete n;
        return tmp;
    }

    Node *tmp = minNode(n->right);
    std::swap(n->v, tmp->v);
    // dummy 只用于接收递归里的 erased;外层 erased 已为 true,不需要再改
    bool dummy = false;
    n->right = eraseRec(n->right, tmp->v, dummy);
    return n;
}

template <typename T>
typename BST<T>::Node* BST<T>::findNode(const T &val) const
{
    Node *tmp = root_;
    while (tmp)
    {
        if (val > tmp->v)
            tmp = tmp->right;
        else if (val < tmp->v)
            tmp = tmp->left;
        else
            return tmp;
    }
    return nullptr;
}

template <typename T>
std::size_t BST<T>::heightRec(const Node *n) const
{
    if (n == nullptr)
        return static_cast<std::size_t>(0);
    return std::max(heightRec(n->left), heightRec(n->right)) + 1;
}

template <typename T>
typename BST<T>::Node* BST<T>::cloneRec(const Node *n)
{
    if (n == nullptr)
        return nullptr;
    Node *tmp = new Node(n->v);
    try
    {
        tmp->left = cloneRec(n->left);
        tmp->right = cloneRec(n->right);
    }
    catch (...)
    {
        // 不能只 delete tmp,左子树可能已克隆,会泄漏
        clearRec(tmp);
        throw;
    }
    return tmp;
}

template <typename T>
template <class Fn>
void BST<T>::inorderRec(Node *n, Fn &fn) const
{
    if (n == nullptr)
        return;
    inorderRec(n->left, fn);
    fn(n->v);
    inorderRec(n->right, fn);
}
template <typename T>
template <class Fn>
void BST<T>::preorderRec(Node *n, Fn &fn) const
{
    if (n == nullptr)
        return;
    fn(n->v);
    preorderRec(n->left, fn);
    preorderRec(n->right, fn);
}
template <typename T>
template <class Fn>
void BST<T>::postorderRec(Node *n, Fn &fn) const
{
    if (n == nullptr)
        return;
    postorderRec(n->left, fn);
    postorderRec(n->right, fn);
    fn(n->v);
}

template<typename T>
BST<T>::~BST() noexcept {
    clear();
}

template<typename T>
BST<T>::BST(const BST& other):
    root_(cloneRec(other.root_)), size_(other.size_) {
}

template<typename T>
BST<T>& BST<T>::operator=(const BST& other) {
    if(this == &other) return *this;
    // 不能先 clear 再 cloneRec,拷贝抛异常时原树已丢
    BST tmp(other);
    swap(tmp);
    return *this;
}

template<typename T>
BST<T>::BST(BST&& other) noexcept : root_(other.root_), size_(other.size_){
    // 不能省略置空 other,否则 other 析构会删除同一棵树,双重释放
    other.root_ = nullptr;
    other.size_ = 0;
}

template<typename T>
BST<T>& BST<T>::operator=(BST&& other) noexcept {
    if(this == &other) return *this;
    clear();
    root_ = other.root_;
    size_ = other.size_;
    // 不能省略置空 other,否则 other 析构会删除已转移的树,双重释放
    other.root_ = nullptr;
    other.size_ = 0;
    return *this;
}

template<typename T>
void BST<T>::swap(BST& other) noexcept {
    std::swap(root_, other.root_);
    std::swap(size_, other.size_);
}

template<typename T>
bool BST<T>::insert(const T& val) {
    bool dummy = false;
    root_ = insertRec(root_, val, dummy);
    if(dummy) size_++;
    return dummy;
}

template<typename T>
bool BST<T>::insert(T&& val) {
    bool dummy = false;
    root_ = insertRec(root_, std::move(val), dummy);
    if(dummy) size_++;
    return dummy;
}

template<typename T>
bool BST<T>::contains(const T& val) const {
    return findNode(val) != nullptr;
}

template<typename T>
const T* BST<T>::find(const T& val) const {
    Node* tmp = findNode(val);
    if(tmp == nullptr) return nullptr;
    return &tmp->v;
}

template<typename T>
const T& BST<T>::minimum() const {
    const Node* tmp = minNode(root_);
    return tmp->v;
}

template<typename T>
const T& BST<T>::maximum() const {
    const Node* tmp = maxNode(root_);
    return tmp->v;
}

template<typename T>
bool BST<T>::erase(const T& val) {
    bool dummy = false;
    root_ = eraseRec(root_, val, dummy);
    if(dummy) size_--;
    return dummy;
}

template<typename T>
template<class Fn> 
void BST<T>::inorder(Fn&& fn) const {
    inorderRec(root_, fn);
}
template<typename T>
template<class Fn> 
void BST<T>::preorder(Fn&& fn) const {
    preorderRec(root_, fn);
}

template<typename T>
template<class Fn>
void BST<T>::postorder(Fn&& fn) const {
    postorderRec(root_, fn);
}

template<typename T>
template<class Fn> 
void BST<T>::levelorder(Fn&& fn) const {
    if(root_ == nullptr) return;
    std::queue<Node*> q;
    q.push(root_);
    while(!q.empty()){
        Node* tmp = q.front();
        q.pop();
        fn(tmp->v);
        if(tmp->left) q.push(tmp->left);
        if(tmp->right) q.push(tmp->right);
    }
}


template<typename T>
std::size_t BST<T>::height() const {
    return heightRec(root_);
}

template<typename T>
void BST<T>::clear() noexcept {
    clearRec(root_);
    root_ = nullptr;
    size_ = 0;
}

#endif








posted @ 2026-11-01 17:37  abnb250  阅读(6)  评论(0)    收藏  举报