当前位置:首页 > 技术 > 正文内容

二叉搜索树的原理与C++实现

访客 技术 2026年6月26日 1

二叉搜索树的基本特性

二叉搜索树(Binary Search Tree, BST)是一种特殊的二叉树结构,它满足以下递归性质:

  • 对于任意节点,其左子树中所有节点的值均小于该节点的值。
  • 其右子树中所有节点的值均大于该节点的值。
  • 左右子树也必须是二叉搜索树。

这种有序性使得二叉搜索树在查找、插入和删除操作上具有天然优势。特别地,对BST进行中序遍历可得到一个升序序列。

核心数据结构设计

使用模板类实现通用性,支持不同类型键值存储:

template<typename T>
struct TreeNode {
    T data;
    TreeNode* left;
    TreeNode* right;

    TreeNode(const T& val) : data(val), left(nullptr), right(nullptr) {}
};

template<typename T>
class BinarySearchTree {
private:
    TreeNode<T>* root;

    // 辅助函数声明
    void inorderTraversal(TreeNode<T>* node) const;
    TreeNode<T>* insertRecursive(TreeNode<T>*& node, const T& value);
    bool searchRecursive(TreeNode<T>* node, const T& target) const;
    TreeNode<T>* findMinNode(TreeNode<T>* node);

public:
    BinarySearchTree() : root(nullptr) {}
    ~BinarySearchTree();
    
    void insert(const T& value);
    bool search(const T& value) const;
    bool remove(const T& value);
    void printInOrder() const;
};

关键操作详解

查找操作

基于比较策略逐层下降:

bool BinarySearchTree::search(const T& value) const {
    TreeNode<T>* current = root;
    while (current != nullptr) {
        if (value == current->data)
            return true;
        else if (value < current->data)
            current = current->left;
        else
            current = current->right;
    }
    return false;
}

插入逻辑

定位空位并创建新节点:

void BinarySearchTree::insert(const T& value) {
    TreeNode<T>** pos = &root;
    while (*pos != nullptr) {
        if (value == (*pos)->data)
            return; // 已存在则不重复插入
        else if (value < (*pos)->data)
            pos = &((*pos)->left);
        else
            pos = &((*pos)->right);
    }
    *pos = new TreeNode<T>(value);
}

删除机制

需处理三种情况:

  1. 叶子节点:直接释放内存并断开连接。
  2. 单侧子树:用子树替代当前节点位置。
  3. 双子树共存:寻找右子树中的最小节点(或左子树最大节点)替换原节点值,再递归删除替代节点。
bool BinarySearchTree::remove(const T& value) {
    TreeNode<T>** current = &root;
    while (*current != nullptr) {
        if (value == (*current)->data)
            break;
        else if (value < (*current)->data)
            current = &((*current)->left);
        else
            current = &((*current)->right);
    }

    if (*current == nullptr) return false;

    TreeNode<T>* nodeToDelete = *current;

    // 情况1: 只有右子树 或 无子树
    if ((*current)->left == nullptr) {
        *current = (*current)->right;
        delete nodeToDelete;
    }
    // 情况2: 只有左子树
    else if ((*current)->right == nullptr) {
        *current = (*current)->left;
        delete nodeToDelete;
    }
    // 情况3: 双子树均存在
    else {
        TreeNode<T>** successor = &((*current)->right);
        while ((*successor)->left != nullptr)
            successor = &((*successor)->left);

        (*current)->data = (*successor)->data;
        nodeToDelete = *successor;
        *successor = (*successor)->right;
        delete nodeToDelete;
    }
    return true;
}

键值映射扩展应用

将BST扩展为KV模型可用于构建字典或计数器:

template<typename Key, typename Value>
class MapBST {
    struct PairNode {
        Key key;
        Value value;
        PairNode* left;
        PairNode* right;
        
        PairNode(const Key& k, const Value& v) 
            : key(k), value(v), left(nullptr), right(nullptr) {}
    };

    PairNode* root;

public:
    void put(const Key& k, const Value& v) {
        PairNode** loc = &root;
        while (*loc) {
            if (k == (*loc)->key) {
                (*loc)->value = v; // 更新已有键
                return;
            } else if (k < (*loc)->key)
                loc = &((*loc)->left);
            else
                loc = &((*loc)->right);
        }
        *loc = new PairNode(k, v);
    }

    Value* get(const Key& k) {
        PairNode* cur = root;
        while (cur) {
            if (k == cur->key) return &(cur->value);
            else if (k < cur->key) cur = cur->left;
            else cur = cur->right;
        }
        return nullptr;
    }
};

实际应用场景示例

词典查询系统

MapBST<string, string> dictionary;
dictionary.put("algorithm", "算法");
dictionary.put("binary", "二进制");

string query = "algorithm";
auto meaning = dictionary.get(query);
if (meaning) cout << *meaning << endl;

频率统计器

MapBST<string, int> wordCount;
vector<string> words = {"apple", "banana", "apple", "cherry"};

for (const auto& w : words) {
    int* count = wordCount.get(w);
    if (count) (*count)++;
    else wordCount.put(w, 1);
}

性能分析与局限性

理想情况下,BST各项操作的时间复杂度为 O(log n),其中 n 为节点数量。然而当数据按顺序插入时,树会退化为链表结构,导致最坏情况下的时间复杂度上升至 O(n)。

为解决此问题,后续发展出平衡二叉树结构如AVL树和红黑树,通过旋转操作维持树的高度平衡,从而保证操作效率稳定。

相关文章

Linux crontab 详解

1) crontab 是什么cron 是 Linux 的定时任务守护进程;crontab 是用来编辑/查看“按时间周期执行命令”的表(cron table)。常见两类:用户 crontab:每个用户一份(crontab -e 编辑)系统级 crontab / cron.d:可指定执行用户(/etc/crontab、/etc/cron.d/*)2) crontab 时间...

富文本里可以允许的 HTML 属性

一、所有标签默认允许的安全属性(极少)class        (可选)id           (通常建议禁用)title️ 注意:id 容易被滥用做锚点注入,很多系统直接禁用class 允许的话最好只允许固定前缀(如 editor-*)二、a 标签允许属性<a href="" t...

Mac 安装 Node.js 指南

方法一:通过官网安装包(最简单,适合初学者)如果你只是想快速安装并开始使用,这是最直接的方法。访问 Node.js 官网。页面会显示两个版本:LTS (Recommended For Most Users):长期支持版,最稳定。建议选这个。Current:最新特性版,包含最新功能但可能不够稳定。下载 .pkg 安装包并运行。按照安装向导点击“下一步”即可完成。方法二:使用 Homebrew 安装(...

Dom\HTML_NO_DEFAULT_NS 的副作用:自动加闭合标签

在使用Dom\HTMLDocument时,Dom\HTML_NO_DEFAULT_NS 将禁止在解析过程中设置元素的命名空间, 此设置是为了与DOMDocument向后兼容而存在的。当使用它时,已知的一个副作用就是:自动加闭合标签例如 </img> 为什么会这样?当你使用:Dom\HTML_NO_DEFAULT_NS文档会变成 无命名空间模式,此时内部更接近 XML...

Laravel 事件和监听器创建

在 Laravel 中,使用 Artisan 命令创建 Events(事件) 和 Listeners(监听器) 是非常高效的。你可以通过以下几种方式来实现:1. 手动创建单个 Event如果你只想创建一个事件类,可以使用 make:event 命令:Bashphp artisan make:event UserRegistered执行后,文件将生成在 app/Even...

自定义域名解析神器 dnsmasq

什么是 dnsmasq?dnsmasq 是一个轻量级、功能强大的网络服务工具,专为小型和中等规模网络设计。它是一个综合的网络基础设施解决方案[1]。dnsmasq 能做什么?功能说明应用场景DNS 转发与缓存将 DNS 查询转发到上游服务器(ISP、Google DNS 等),并在本地缓存结果加快 DNS 查询速度,减少外部 DNS 流量本地 DNS解析本地网络设备的主机名,无需编辑&n...

发表评论

访客

◎欢迎参与讨论,请在这里发表您的看法和观点。