C++ 二叉搜索树详解

概念与特性

二叉搜索树(Binary Search Tree, BST),又称二叉排序树,是一种高效的数据存储与检索结构。它具有以下核心特性:

  1. 有序性:对于树中的任意节点:

    • 左子树所有节点的值 ≤ 当前节点的值
    • 右子树所有节点的值 ≥ 当前节点的值
  2. 递归结构:左右子树本身也是二叉搜索树

  3. 重复值处理:根据实现需求,可以允许或禁止重复值插入。在C++标准库中,mapset不允许重复,而multimapmultiset允许。

性能分析

二叉搜索树的性能高度依赖于树的平衡程度:

  • 最优情况(完全或接近完全二叉树):查找、插入、删除操作的时间复杂度为O(log₂N)
  • 最差情况(退化为链表):时间复杂度恶化为O(N)
    在这里插入图片描述

与二分查找相比,二叉搜索树有以下优势:

  1. 不需要连续存储空间
  2. 支持高效动态插入和删除
  3. 适用于内存中的数据组织

核心操作详解

插入操作

插入算法遵循以下步骤:

  1. 若树为空,创建新节点作为根节点
  2. 若树非空:
    • 新值 < 当前节点值 → 向左子树递归
    • 新值 > 当前节点值 → 向右子树递归
    • 对于允许重复值的情况,需保持一致的插入方向(始终左或始终右)
bool Insert(const K& key) {
    if (_root == nullptr) {
        _root = new Node(key);
        return true;
    }
    
    Node* parent = nullptr;
    Node* cur = _root;
    while (cur) {
        parent = cur;
        if (key < cur->_key) {
            cur = cur->_left;
        } else if (key > cur->_key) {
            cur = cur->_right;
        } else {
            return false; // 重复值处理
        }
    }
    
    cur = new Node(key);
    if (key < parent->_key) {
        parent->_left = cur;
    } else {
        parent->_right = cur;
    }
    return true;
}

查找操作

查找过程从根节点开始:

  1. 目标值 < 当前节点 → 向左子树查找
  2. 目标值 > 当前节点 → 向右子树查找
  3. 找到匹配值或到达空节点终止

对于允许重复值的情况,通常需要返回中序遍历中的第一个匹配节点。如下图,查找3,要
找到1的右孩子的那个3返回
在这里插入图片描述

删除操作

删除操作分为四种情况处理:

  1. 叶子节点:直接删除
  2. 只有右子树:用右孩子替代被删节点
  3. 只有左子树:用左孩子替代被删节点
  4. 左右子树都存在
    • 找到右子树的最小节点(或左子树的最大节点)
    • 交换两节点值
    • 删除替代节点(此时必为情况1或2)
bool Erase(const K& key) {
    // 查找过程...
    
    // 找到节点后的处理
    if (cur->_left == nullptr) {
        // 处理右子树或叶子节点情况
    } else if (cur->_right == nullptr) {
        // 处理左子树情况
    } else {
        // 处理双子树情况 - 找右子树最小节点
        Node* rightMinParent = cur;
        Node* rightMin = cur->_right;
        while (rightMin->_left) {
            rightMinParent = rightMin;
            rightMin = rightMin->_left;
        }
        
        cur->_key = rightMin->_key; // 值替换
        // 删除rightMin节点(必为情况1或2)
    }
}

应用场景

纯Key场景

适用于只需判断元素是否存在的场景:

  • 场景1:小区无人值守车库,小区车库买了车位的业主车才能进小区,那么物业会把买了车位业主的车牌号录入后台系统,车辆进入时扫描车牌在不在系统中,在则抬杆,不在则提示非本小区车辆,无法进入。
  • 场景2:检查一篇英文文章单词拼写是否正确,将词库中所有单词放入二叉搜索树,读取文章的单词,查找是否在二叉搜索树中,不在则波浪线标红提示。

Key-Value场景

适用于需要关联数据的场景:

  • 场景1:简单中英互译字典,树的结构中(结点)存储key(英文)和vlaue(中文),搜索时输入英文,同时查找到了英文对应的中文。
  • 场景2:商场无人值守车库,入口进场时扫描车牌,记录车牌和入场时间,出口离场时,扫描牌,查找入场时间,用当前时间减去入场时间计算出停车时长,计算出停车费用,缴费后抬杆,辆离场。
  • 场景3:统计一篇文章中单词出现的次数,读取一个单词,查找单词是否存在,不存在这个说第一次出现,(单词,1),单词存在,则++单词对应的次数。
// Key-Value节点结构
template<class K, class V>
struct BSTNode {
    K _key;
    V _value;
    BSTNode<K,V>* _left;
    BSTNode<K,V>* _right;
    
    BSTNode(const K& key, const V& value)
        : _key(key), _value(value), _left(nullptr), _right(nullptr) {}
};

纯Key版本和KV版本二叉搜索树实现

#pragma once
#include <iostream>
using namespace std;
// 纯键(Key)版本的二叉搜索树
namespace K 
{
    // 二叉搜索树节点结构
    template <class K>
    struct BSTNode 
    {
        K _key;                // 节点存储的键值
        BSTNode<K>* _left;     // 左子节点指针
        BSTNode<K>* _right;    // 右子节点指针
        
        // 构造函数
        BSTNode(const K& key)
            : _key(key)
            , _left(nullptr)
            , _right(nullptr) 
        {}
    };

    // 二叉搜索树类
    template <class K>
    class BSTree 
    {
        using Node = BSTNode<K>;  // 类型别名,方便使用
        
    public:
        BSTree() : _root(nullptr) {}  // 默认构造函数
        
        // 插入键值
        bool Insert(const K& key) 
        {
            if (_root == nullptr) 
            {
                _root = new Node(key);  // 树为空时直接创建根节点
                return true;
            }
            
            Node* cur = _root;
            Node* parent = nullptr;
            
            // 查找插入位置
            while (cur) 
            {
                parent = cur;
                if (key < cur->_key) 
                {
                    cur = cur->_left;   // 向左子树查找
                } 
                else if (key > cur->_key) 
                {
                    cur = cur->_right;  // 向右子树查找
                } 
                else 
                {
                    return false;  // 键值已存在,插入失败
                }
            }
            
            // 创建新节点并插入
            cur = new Node(key);
            if (key < parent->_key) 
            {
                parent->_left = cur;   // 作为左子节点
            } 
            else 
            {
                parent->_right = cur;  // 作为右子节点
            }
            return true;
        }
        
        // 查找键值是否存在
        bool Find(const K& key) 
        {
            Node* cur = _root;
            while (cur) 
            {
                if (key < cur->_key) 
                {
                    cur = cur->_left;
                } 
                else if (key > cur->_key) 
                {
                    cur = cur->_right;
                } 
                else 
                {
                    return true;  // 找到键值
                }
            }
            return false;  // 未找到键值
        }
        
        // 删除键值
        bool Erase(const K& key) 
        {
            Node* cur = _root;
            Node* parent = nullptr;
            
            // 查找要删除的节点
            while (cur) 
            {
                if (key < cur->_key) 
                {
                    parent = cur;
                    cur = cur->_left;
                } 
                else if (key > cur->_key) 
                {
                    parent = cur;
                    cur = cur->_right;
                } 
                else 
                {
                    // 找到要删除的节点,分三种情况处理
                    if (cur->_left == nullptr)  // 情况1:左子节点为空
                    {
                        if (cur == _root) 
                        {
                            _root = cur->_right;  // 删除的是根节点
                        } 
                        else 
                        {
                            if (parent->_left == cur) 
                            {
                                parent->_left = cur->_right;
                            } 
                            else 
                            {
                                parent->_right = cur->_right;
                            }
                        }
                        delete cur;
                    } 
                    else if (cur->_right == nullptr)  // 情况2:右子节点为空
                    {
                        if (cur == _root) 
                        {
                            _root = cur->_left;
                        } 
                        else 
                        {
                            if (parent->_left == cur) 
                            {
                                parent->_left = cur->_left;
                            } 
                            else 
                            {
                                parent->_right = cur->_left;
                            }
                        }
                        delete cur;
                    } 
                    else  // 情况3:左右子节点都不为空
                    {
                        // 找到右子树的最小节点(替代节点)
                        Node* replace = cur->_right;
                        Node* replace_parent = cur;
                        while (replace->_left) 
                        {
                            replace_parent = replace;
                            replace = replace->_left;
                        }
                        
                        // 用替代节点的值替换当前节点值
                        cur->_key = replace->_key;
                        
                        // 删除替代节点
                        if (replace_parent->_left == replace) 
                        {
                            replace_parent->_left = replace->_right;
                        } 
                        else 
                        {
                            replace_parent->_right = replace->_right;
                        }
                        delete replace;
                    }
                    return true;  // 删除成功
                }
            }
            return false;  // 未找到要删除的键值
        }
        
        // 中序遍历打印树内容
        void InOrder() 
        {
            _InOrder(_root);
            std::cout << std::endl;
        }
        
    private:
        // 递归中序遍历辅助函数
        void _InOrder(Node* root) 
        {
            if (root == nullptr) return;
            _InOrder(root->_left);
            std::cout << root->_key << " ";
            _InOrder(root->_right);
        }
        
        Node* _root = nullptr;  // 根节点指针
    };
}

// 键值对(Key-Value)版本的二叉搜索树
namespace KV 
{
    // 键值对节点结构
    template <class K, class V>
    struct BSTNode 
    {
        K _key;                // 键
        V _value;              // 值
        BSTNode<K,V>* _left;   // 左子节点
        BSTNode<K,V>* _right;  // 右子节点
        
        // 构造函数
        BSTNode(const K& key, const V& value)
            : _key(key)
            , _value(value)
            , _left(nullptr)
            , _right(nullptr) 
        {}
    };

    // 键值对二叉搜索树类
    template <class K, class V>
    class BSTree 
    {
        using Node = BSTNode<K,V>;  // 类型别名
        
    public:
        BSTree() = default;  // 默认构造函数
        
        // 拷贝构造函数
        BSTree(const BSTree<K,V>& other) 
        {
            _root = Copy(other._root);
        }
        
        // 赋值运算符重载(使用拷贝交换惯用法)
        BSTree<K,V>& operator=(BSTree<K,V> other) 
        {
            std::swap(_root, other._root);
            return *this;
        }
        
        // 析构函数
        ~BSTree() 
        {
            Destroy(_root);
            _root = nullptr;
        }
        
        // 插入键值对
        bool Insert(const K& key, const V& value) 
        {
            if (_root == nullptr) 
            {
                _root = new Node(key, value);
                return true;
            }
            
            Node* cur = _root;
            Node* parent = nullptr;
            
            // 查找插入位置
            while (cur) 
            {
                parent = cur;
                if (key < cur->_key) 
                {
                    cur = cur->_left;
                } 
                else if (key > cur->_key) 
                {
                    cur = cur->_right;
                } 
                else 
                {
                    return false;  // 键已存在,插入失败
                }
            }
            
            // 创建新节点并插入
            cur = new Node(key, value);
            if (key < parent->_key) 
            {
                parent->_left = cur;
            } 
            else 
            {
                parent->_right = cur;
            }
            return true;
        }
        
        // 查找键对应的节点
        Node* Find(const K& key) 
        {
            Node* cur = _root;
            while (cur) 
            {
                if (key < cur->_key) 
                {
                    cur = cur->_right;
                } 
                else if (key > cur->_key) 
                {
                    cur = cur->_left;
                } 
                else 
                {
                    return cur;  // 返回找到的节点指针
                }
            }
            return nullptr;  // 未找到返回空指针
        }
        
        // 删除键值对
        bool Erase(const K& key) 
        {
            Node* cur = _root;
            Node* parent = nullptr;
            
            // 查找要删除的节点
            while (cur) 
            {
                if (key < cur->_key) 
                {
                    parent = cur;
                    cur = cur->_right;
                } 
                else if (key > cur->_key) 
                {
                    parent = cur;
                    cur = cur->_left;
                } 
                else 
                {
                    // 找到要删除的节点,分三种情况处理
                    if (cur->_left == nullptr)  // 情况1:左子节点为空
                    {
                        if (cur == _root) 
                        {
                            _root = cur->_right;
                        } 
                        else 
                        {
                            if (parent->_left == cur) 
                            {
                                parent->_left = cur->_right;
                            } 
                            else 
                            {
                                parent->_right = cur->_right;
                            }
                        }
                        delete cur;
                    } 
                    else if (cur->_right == nullptr)  // 情况2:右子节点为空
                    {
                        if (cur == _root) 
                        {
                            _root = cur->_left;
                        } 
                        else 
                        {
                            if (parent->_left == cur) 
                            {
                                parent->_left = cur->_left;
                            } 
                            else 
                            {
                                parent->_right = cur->_left;
                            }
                        }
                        delete cur;
                    } 
                    else  // 情况3:左右子节点都不为空
                    {
                        // 找到右子树的最小节点(替代节点)
                        Node* replace = cur->_right;
                        Node* replace_parent = cur;
                        while (replace->_left) 
                        {
                            replace_parent = replace;
                            replace = replace->_left;
                        }
                        
                        // 用替代节点的键值替换当前节点键值
                        cur->_key = replace->_key;
                        cur->_value = replace->_value;
                        
                        // 删除替代节点
                        if (replace_parent->_left == replace) 
                        {
                            replace_parent->_left = replace->_right;
                        } 
                        else 
                        {
                            replace_parent->_right = replace->_right;
                        }
                        delete replace;
                    }
                    return true;  // 删除成功
                }
            }
            return false;  // 未找到要删除的键
        }
        
        // 中序遍历打印树内容
        void InOrder() 
        {
            _InOrder(_root);
            std::cout << std::endl;
        }
        
    private:
        // 递归中序遍历辅助函数
        void _InOrder(Node* root) 
        {
            if (root == nullptr) return;
            _InOrder(root->_left);
            std::cout << root->_key << ":" << root->_value << std::endl;
            _InOrder(root->_right);
        }
        
        // 递归销毁树辅助函数
        void Destroy(Node* root) 
        {
            if (root == nullptr) return;
            Destroy(root->_left);
            Destroy(root->_right);
            delete root;
        }
        
        // 递归拷贝树辅助函数
        Node* Copy(Node* root) 
        {
            if (root == nullptr) return nullptr;
            Node* newRoot = new Node(root->_key, root->_value);
            newRoot->_left = Copy(root->_left);
            newRoot->_right = Copy(root->_right);
            return newRoot;
        }
        
        Node* _root = nullptr;  // 根节点指针
    };
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值