【C++】 二叉搜索树


深度剖析二叉搜索树(BST)

一、核心定义

二叉搜索树 Binary Search Tree,简称 BST满足左小右大严格规则的二叉树:

  1. 左子树所有节点值 < 根节点值
  2. 右子树所有节点值 > 根节点值
  3. 左右子树也必须是二叉搜索树
  4. 无重复节点(默认规则)

二、五大核心性质

  1. 中序遍历 = 升序有序序列(BST 最关键特性)
  2. 最小值:一路向左走到最左叶子
  3. 最大值:一路向右走到最右叶子
  4. 前驱节点:比当前节点小的最大节点
  5. 后继节点:比当前节点大的最小节点

三、四大基础操作(原理 + 流程)

1. 查找节点

逻辑:比根小走左,比根大走右,相等找到

  • 时间复杂度:O(h) h = 树高
  • 最优 O (logn)
  • 最坏 O (n)(斜树)

2. 插入节点

  1. 从根开始比较
  2. 小于根走左,大于走右
  3. 走到空位置直接挂载
  4. 不改变原有树结构

3. 删除节点**(最难!分 3 种情况)**

情况 1:删除叶子节点

直接置空,无任何影响

情况 2:删除只有一个子树节点

用唯一子节点顶替被删节点

情况 3:删除左右都有子树节点(核心)

两种方案二选一

  1. 找右子树最小值替换,删除该最小值
  2. 找左子树最大值替换,删除该最大值目的:保留 BST 有序性

4. 遍历

  • 前序:根→左→右
  • 中序:左→根→右 (升序)
  • 后序:左→右→根
  • 层序:从上到下逐层遍历

代码实现

1. 节点结构设计

复制代码
template<class K>
struct BSTNode
{
    K _key;
    BSTNode<K>* _left;
    BSTNode<K>* _right;

    BSTNode(const K& key)
        :_key(key)
        ,_left(nullptr)
        ,_right(nullptr)
    { }
};

2. 二叉搜索树类

复制代码
template<class K>
class BSTree {
    typedef BSTNode<K> Node;
private:
    Node* _root = nullptr;
};

3. 核心功能解析

1️⃣ 插入(Insert)

复制代码
bool Insert(const K& key)
{
	if (_root == nullptr)
	{
		_root = new Node(key);
		return true;
	}
	Node* parent = nullptr;
	Node* cur = _root;
	while (cur)
	{    //查找是否有相同的值,判断是否插入
		if (cur->_key < key)
		{
			parent = cur;
			cur = cur->_right;
		}
		else if (cur->_key > key)
		{
			parent = cur;
			cur = cur->_left;
		}
		else
		{
			return false;
		}
	}
	cur = new Node(key);
	if (parent->_key < key)
	{
		parent->_right = cur;
	}
	else
	{
		parent->_left = cur;
	}

	return true;
}

思想:

  • 若树为空,直接作为根节点

  • 否则从根开始比较:

    • 小 → 往左走

    • 大 → 往右走

    • 相等 → 插入失败(不允许重复)

2️⃣ 查找(Find)

复制代码
bool Find(const K& key)
{
	Node* cur = _root;
	while (cur)
	{
		if (cur->_key < key)
		{
			cur = cur->_right;
		}
		else if (cur->_key > key)
		{
			cur = cur->_left;
		}
		else
		{
			return true;
		}
	}

	return false;
}

3️⃣ 删除(Erase)------最复杂的部分

✅ 情况一:左子树为空

cpp 复制代码
if (cur->_left == nullptr)
{
	if (cur != _root)
	{
	// 左为空,父亲指向我的右
	    if (cur == parent->_left)
		{
			parent->_left = cur->_right;
		}
		else
		{
			parent->_right = cur->_right;
		}
	}
	else
	{
		_root = cur->_right;
	}
	delete cur;
    return true;
}

**✅**情况二:右子树为空

cpp 复制代码
else if (cur->_right == nullptr)
{
	if (cur != _root)
	{
			// 右为空,父亲指向我的左
	    if (cur == parent->_left)
	    {
		    parent->_left = cur->_left;
	    }
	    else
	    {
		    parent->_right = cur->_left;
	    }
	}
	else
	{
		_root = cur->_left;
	}
	delete cur;
    return true;
}

**✅**情况三:左右子树都不为空(重点)

**策略:**替换删除法

  1. 找到右子树中的最小节点(最左节点)

  2. 用该节点的值替换待删除节点

  3. 删除那个最小节点

cpp 复制代码
else
{
	Node* minRightParent = cur;//为什么这里不置空?思考下
	Node* minRight = cur->_right;
	while (minRight->_left)
	{
		minRightParent = minRight;
		minRight = minRight->_left;
	}
	swap(cur->_key, minRight->_key);

	if (minRight == minRightParent->_left)
		minRightParent->_left = minRight->_right;
	else
	minRightParent->_right = minRight->_right;

	delete minRight;
    return true;
}

✅完整代码

cpp 复制代码
bool Erase(const K& key)
{
	Node* parent = nullptr;
	Node* cur = _root;
	while (cur)
	{
		if (cur->_key < key)
		{
			parent = cur;
			cur = cur->_right;
		}
		else if (cur->_key > key)
		{
			parent = cur;
			cur = cur->_left;
		}
		else
		{
			if (cur->_left == nullptr)
			{
				if (cur != _root)
				{
					// 左为空,父亲指向我的右
					if (cur == parent->_left)
					{
						parent->_left = cur->_right;
					}
					else
					{
						parent->_right = cur->_right;
					}
				}
				else
				{
					_root = cur->_right;
				}
                delete cur;
                
			}
			else if (cur->_right == nullptr)
			{
				if (cur != _root)
				{
					// 右为空,父亲指向我的左
					if (cur == parent->_left)
					{
						parent->_left = cur->_left;
					}
					else
					{
						parent->_right = cur->_left;
					}
				}
				else
				{
					_root = cur->_left;
				}
				delete cur;
               
			}

			else
			{
				Node* minRightParent = cur;
				Node* minRight = cur->_right;
				while (minRight->_left)
				{
					minRightParent = minRight;
					minRight = minRight->_left;
				}
				swap(cur->_key, minRight->_key);

				if (minRight == minRightParent->_left)
					minRightParent->_left = minRight->_right;
				else
					minRightParent->_right = minRight->_right;

				delete minRight;
			}
			return true;
		}
	}
	return false;
}

4️⃣ 中序遍历(InOrder)

cpp 复制代码
public:	
    void InOrder()
	{
		_InOrder(_root);
		cout << endl;
	}

private:
	void _InOrder(Node* root)
	{
		if (root == nullptr)
			return;

		_InOrder(root->_left);
		cout << root->_key << " ";
		_InOrder(root->_right);
	}
  • 将遍历功能分为公有对外接口函数与私有递归辅助函数
  • 对外 InOrder() 无参数,作为调用入口
  • 私有 _InOrder(Node*) 接收当前节点,实现递归遍历逻辑
  • 外部调用公有接口,接口内部自动传入根节点调用递归函数
  • 有效隐藏树节点指针,符合 C++ 类封装设计思想

完整代码

cpp 复制代码
#pragma once
#include <iostream>
using namespace std;

namespace key
{
	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() = default;

		BSTree(const BSTree& t)
		{
			_root = Copy(t._root);
		}

		BSTree& operator=(BSTree tmp)
		{
			swap(_root, tmp._root);
			return *this;
		}

		~BSTree()
		{
			Destroy(_root);
			_root = nullptr;
		}

		bool Insert(const K& key)
		{
			if (_root == nullptr)
			{
				_root = new Node(key);
				return true;
			}

			Node* parent = nullptr;
			Node* cur = _root;

			while (cur)
			{
				if (cur->_key < key)
				{
					parent = cur;
					cur = cur->_right;
				}
				else if (cur->_key > key)
				{
					parent = cur;
					cur = cur->_left;
				}
				else
				{
					return false;
				}
			}

			cur = new Node(key);
			if (parent->_key < key)
				parent->_right = cur;
			else
				parent->_left = cur;

			return true;
		}

		bool find(const K& key)
		{
			Node* cur = _root;
			while (cur)
			{
				if (cur->_key < key)
					cur = cur->_right;
				else if (cur->_key > key)
					cur = cur->_left;
				else
					return true;
			}
			return false;
		}

		bool Erase(const K& key)
		{
			Node* parent = nullptr;
			Node* cur = _root;
			while (cur)
			{
				if (cur->_key < key)
				{
					parent = cur;
					cur = cur->_right;
				}
				else if (cur->_key > key)
				{
					parent = cur;
					cur = cur->_left;
				}
				else
				{
					if (cur->_left == nullptr)
					{
						if (parent == nullptr)
							_root = cur->_right;
						else
						{
							if (parent->_left == cur)
								parent->_left = cur->_right;
							else
								parent->_right = cur->_right;
						}
						delete cur;
						return true;
					}
					else if (cur->_right == nullptr)
					{
						if (parent == nullptr)
							_root = cur->_left;
						else
						{
							if (parent->_left == cur)
								parent->_left = cur->_left;
							else
								parent->_right = cur->_left;
						}
						delete cur;
						return true;
					}
					else
					{
						Node* replaceParent = cur;
						Node* replace = cur->_right;
						while (replace->_left)
						{
							replaceParent = replace;
							replace = replace->_left;
						}

						cur->_key = replace->_key;

						if (replaceParent->_left == replace)
							replaceParent->_left = replace->_right;
						else
							replaceParent->_right = replace->_right;

						delete replace;
						return true;
					}
				}
			}
			return false;
		}

		void Inorder()
		{
			_Inorder(_root);
			cout << endl;
		}

	private:
		void _Inorder(Node* root)
		{
			if (root == nullptr)
				return;
			_Inorder(root->_left);
			cout << root->_key << " ";
			_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);
			newRoot->_left = Copy(root->_left);
			newRoot->_right = Copy(root->_right);
			return newRoot;
		}

		Node* _root = nullptr;
	};
}

⼆叉搜索树key和key/value使⽤场景

类型 排序依据 存储内容 典型用途
纯 key key 本身 仅关键字 查重、排序、元素判定
key-value key 字段 关键字 + 业务数据 映射查找、数据索引

场景 1:简易中英互译词典

场景 2:统计水果出现次数


完整代码

cpp 复制代码
namespace 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)
        {}
    };

    template<class K, class V>
    class BSTree
    {
        using Node = BSTNode<K, V>;
    public:
        BSTree() = default;

        BSTree(const BSTree& t)
        {
            _root = Copy(t._root);
        }

        BSTree& operator=(BSTree tmp)
        {
            swap(_root, tmp._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* parent = nullptr;
            Node* cur = _root;
            while (cur)
            {
                if (cur->_key < key)
                {
                    parent = cur;
                    cur = cur->_right;
                }
                else if (cur->_key > key)
                {
                    parent = cur;
                    cur = cur->_left;
                }
                else
                {
                    return false;
                }
            }

            cur = new Node(key, value);
            if (parent->_key < key)
                parent->_right = cur;
            else
                parent->_left = cur;

            return true;
        }

        Node* find(const K& key)
        {
            Node* cur = _root;
            while (cur)
            {
                if (cur->_key < key)
                    cur = cur->_right;
                else if (cur->_key > key)
                    cur = cur->_left;
                else
                    return cur;
            }
            return nullptr;
        }

        bool Erase(const K& key)
        {
            Node* parent = nullptr;
            Node* cur = _root;
            while (cur)
            {
                if (cur->_key < key)
                {
                    parent = cur;
                    cur = cur->_right;
                }
                else if (cur->_key > key)
                {
                    parent = cur;
                    cur = cur->_left;
                }
                else
                {
                    if (cur->_left == nullptr)
                    {
                        if (parent == nullptr)
                            _root = cur->_right;
                        else
                        {
                            if (parent->_left == cur)
                                parent->_left = cur->_right;
                            else
                                parent->_right = cur->_right;
                        }
                        delete cur;
                        return true;
                    }
                    else if (cur->_right == nullptr)
                    {
                        if (parent == nullptr)
                            _root = cur->_left;
                        else
                        {
                            if (parent->_left == cur)
                                parent->_left = cur->_left;
                            else
                                parent->_right = cur->_left;
                        }
                        delete cur;
                        return true;
                    }
                    else
                    {
                        Node* replaceParent = cur;
                        Node* replace = cur->_right;
                        while (replace->_left)
                        {
                            replaceParent = replace;
                            replace = replace->_left;
                        }

                        cur->_key = replace->_key;
                        cur->_value = replace->_value;

                        if (replaceParent->_left == replace)
                            replaceParent->_left = replace->_right;
                        else
                            replaceParent->_right = replace->_right;

                        delete replace;
                        return true;
                    }
                }
            }
            return false;
        }

        void Inorder()
        {
            _Inorder(_root);
            cout << endl;
        }

    private:
        void _Inorder(Node* root)
        {
            if (root == nullptr)
                return;
            _Inorder(root->_left);
            cout << root->_key << ":" << root->_value << 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 小时前
C语言 结构体内存对齐规则(通俗易懂版)
c语言·开发语言
吃好睡好便好10 小时前
在Matlab中绘制横直方图
开发语言·学习·算法·matlab
星寂樱易李10 小时前
iperf3 + Python-- 网络带宽、网速、网络稳定性
开发语言·网络·python
仰泳之鹅10 小时前
【C语言】自定义数据类型2——联合体与枚举
c语言·开发语言·算法
之歆10 小时前
DAY_12JavaScript DOM 完全指南(二):实战与性能篇
开发语言·前端·javascript·ecmascript
于小猿Sup11 小时前
VMware在Ubuntu22.04驱动Livox Mid360s
linux·c++·嵌入式硬件·自动驾驶
cen__y11 小时前
Linux12(Git01)
linux·运维·服务器·c语言·开发语言·git
AI人工智能+电脑小能手12 小时前
【大白话说Java面试题 第65题】【JVM篇】第25题:谈谈对 OOM 的认识
java·开发语言·jvm
社交怪人12 小时前
【算平均分】信息学奥赛一本通C语言解法(题号2071)
c语言·开发语言