news 2026/4/16 11:06:21

【C++】2.3 二叉搜索树的实现(附代码)

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
【C++】2.3 二叉搜索树的实现(附代码)

二叉搜索树

左子树<=根,右子树>=根

根据需求不同,等于的元素可能会被去重也可能会被留下

这样查找一个数就可以只遍历一次,数大选哪个右走,小往左走

查找效率:ologn~on

改进:AVL树,红黑树,B树系列,查找效率:ologn

模拟实现(不多插入相同元素)


一.Key结构

1. 节点的定义

template<class K> struct bsnode { K _key; bsnode* _left; bsnode* _right; bsnode(const K& key) :_key(key) ,_left(nullptr) ,_right(nullptr) {} };

2. 成员

typedef bsnode<K> node; node* _root = nullptr;
这样,可以不用写构造函数

3. 二叉树的插入

bool insert(const K& key) { if (_root == nullptr) { _root = new node(key); return 1; } node* cur = _root; node* par = nullptr; while (cur != nullptr) { if (cur->_key < key) { par = cur; cur = cur->_right; } else if (cur->_key > key) { par = cur; cur = cur->_left; } else { return 0; } } node* newnode = new node(key); if (par->_key < key) { par->_right = newnode; } else { par->_left = newnode; } return 1; }

方案:定一个cur的指针,当根节点小于插入元素,向右走,大于则向左走

如果已经有这个数了,则返回false

如果cur为空,则构造一个节点,和cur最后一次经过的节点连接,返回true

那么,cur已经指向空了,怎么才能知道cur最后一次经过的节点?

在额外弄一个par指针尾随cur即可

4. 树排序

二叉搜索树的中序遍历(左->中->右)的特点:为从小到大的数组

因此,可以中序遍历得到有序数组,叫做树排序

void inorder() { _inorder(_root); } void _inorder(node* root) { if (root == nullptr)return; _inorder(root->_left); std::cout << root->_key << " "; _inorder(root->_right); }

5. 查找

和插入同理,但是要找的值大于节点向右走,小于往左走

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 1; } } return 0; }

6. 删除(重要)

删除的集中情况(设删除节点的名称为M)

  1. M子节点左右均空:直接删除即可

  2. M子节点一个为空:M父节点指向M的一个子节点,再删除M

  3. M左右节点都为空:
    替换删除法:将M节点用下面的节点交换,再删除下面的数字节点

    那么和哪个节点交换既可以做到快速删除又可以做到保持左子树<=根,右子树>=根的性质呢
    和左子树的最右节点(最大节点)或右子树的最左节点(最小节点)

    (1)删除快速:由于两个情况都是最左或最右节点,因此一定满足删除方案1或2,因此删除简单

    (2)由于两个情况要么是小的最大,要么是大的最小,因此性质也是满足的

    下面用右边最左来演示

bool erase(const K& _key) { node* par = nullptr; node* cur = _root; while (cur) { if (cur->_key < key) { par = cur; cur = cur->_right; } else if (cur->_key > key) { par = cur; cur = cur->_left; } else { if (cur->_left == nullptr) { if (cur == _root) { _root = cur->_right; } else { if (par->_left == cur) { par->_left = cur->_right; } else { par->_right = cur->_right; } } delete cur; } else if (cur->_right == nullptr) { if (cur == _root) { _root = cur->_left; } else { if (par->_left == cur) { par->_left = cur->_left; } else { par->_right = cur->_left; } } delete cur; } else { node* rp = cur; node* r = cur->_right; while (r->_left) { rp = r; r = r->_left; } cur->_key = r->_key; if (rp->_left == r) { rp->_left = r->_right; } else { rp->_right = r->_right; } delete r; } return 1; } } return 0; }

代码逻辑

  1. 先找值为val的节点(找不到返回0),设找到的节点为M

  2. 节点M左为空:

    (1)节点M为根节点:直接将右节点设为根节点(有没有右节点都无所谓)

    (2)M不为根节点:

    A.M为左节点:M根节点连接M右节点,删M

    B.M为右节点:M根节点连接M右节点,删M

  3. 节点M右为空,同上:

    (1)节点M为根节点:直接将左节点设为根节点

    (2)M不为根节点:

    A.M为左节点:M根节点连接M左节点,删M

    B.M为右节点:M根节点连接M左节点,删M

  4. M左右都不为空(即else)

    先去寻找右子树的最左节点(设为r,r的父节点设为rp)
    由于r为最左节点,因此左边不会有子节点

    (1)正常情况:r为rp左节点
    交换r和M的值,再删r节点连rp和r子节点

    (2)反常情况:M的子节点只有右节点
    此时M就是rp,r为rp右节点,和正常情况恰好相反
    交换r和M的值,再删r节点连rp和r子节点


二.Key-value结构

由于二叉树不一定每一个成员代表的都是1,因此用value记录数值

所有程序和key结构一样,只是模板,节点构造函数多了value而已

template<class K,class V> struct bsnode { K _key; V _val; bsnode* _left; bsnode* _right; bsnode(const K& key,const V& val) :_key(key) ,_val(val) ,_left(nullptr) ,_right(nullptr) { } };

三.构造,析构函数

1. 前序遍历拷贝构造

先拷贝值,再拷贝子节点

由于拷贝构造无法递归,因此先写前序遍历再复用

递归函数

node* copy(node* root) { if (root == nullptr)return nullptr; node* newroot = new node(root->_key, root->_val); newroot->_left = copy(root->_left); newroot->_right = copy(root->_right); return newroot; }

拷贝构造

bstree(const bstree& t) { _root = copy(t._root); }

由于写了拷贝构造就不会生成默认构造,因此强制生成

bstree() = default;

2. 后序析构函数

先析构子节点,再析构自身

由于析构无法递归,因此先写前序遍历再复用

void destroy(node*root) { if (root==nullptr) { return; } destroy(root->_left); destroy(root->_right); delete root; root = nullptr; } ~bstree() { destroy(_root); _root = nullptr; }

3. 赋值重载

现代写法,直接交换,再销毁原来的

bstree&operator=(bstree t) { std::swap(_root, t._root); return *this; }

代码

#include<iostream> #include<assert.h> namespace key { template<class K> struct bsnode { K _key; bsnode* _left; bsnode* _right; bsnode(const K&key) :_key(key ) ,_left(nullptr) ,_right(nullptr) { } }; template<class K> class bstree { public: typedef bsnode<K> node; bool insert(const K& key) { if (_root == nullptr) { _root = new node(key); return 1; } node* cur = _root; node* par = nullptr; while (cur != nullptr) { if (cur->_key < key) { par = cur; cur = cur->_right; } else if (cur->_key > key) { par = cur; cur = cur->_left; } else { return 0; } } node* newnode = new node(key); if (par->_key < key) { par->_right = newnode; } else { par->_left = newnode; } return 1; } void inorder() { _inorder(_root); } //bool find(const K& val) //{ // node* cur = _root; //} bool find(int _key) { node* cur = _root; while (cur) { if (_key < cur->_key) { cur = cur->_left; } else if (_key > cur->_key) { cur = cur->_right; } else { return 1; } } return 0; } bool erase(int key) { node* par = nullptr; node* cur = _root; while (cur) { if (cur->_key < key) { par = cur; cur = cur->_right; } else if (cur->_key > key) { par = cur; cur = cur->_left; } else { if (cur->_left == nullptr) { if (cur == _root) { _root = cur->_right; } else { if (par->_left == cur) { par->_left = cur->_right; } else { par->_right = cur->_right; } } delete cur; } else if (cur->_right == nullptr) { if (cur == _root) { _root = cur->_left; } else { if (par->_left == cur) { par->_left = cur->_left; } else { par->_right = cur->_left; } } delete cur; } else { node* rp = cur; node* r = cur->_right; while (r->_left) { rp = r; r = r->_left; } cur->_key = r->_key; if (rp->_left == r) { rp->_left = r->_right; } else { rp->_right = r->_right; } delete r; } return 1; } } return 0; } private: void _inorder(node* root) { if (root == nullptr)return; _inorder(root->_left); std::cout << root->_key << " "; _inorder(root->_right); } node* _root = nullptr; }; } namespace key_value { template<class K,class V> struct bsnode { K _key; V _val; bsnode* _left; bsnode* _right; bsnode(const K& key,const V& val) :_key(key) ,_val(val) , _left(nullptr) , _right(nullptr) { } }; template<class K,class V> class bstree { public: typedef bsnode<K,V> node; bstree(const bstree& t) { _root = copy(t._root); } bstree() = default; bstree&operator=(bstree t) { std::swap(_root, t._root); return *this; } bool insert(const K& key, const V& val) { if (_root == nullptr) { _root = new node(key,val); return 1; } node* cur = _root; node* par = nullptr; while (cur != nullptr) { if (cur->_key < key) { par = cur; cur = cur->_right; } else if (cur->_key > key) { par = cur; cur = cur->_left; } else { return 0; } } node* newnode = new node(key,val); if (par->_key < key) { par->_right = newnode; } else { par->_left = newnode; } return 1; } void inorder() { _inorder(_root); } //bool find(const K& val) //{ // node* cur = _root; //} node* 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 cur; } } return nullptr; } bool erase(const K& key) { node* par = nullptr; node* cur = _root; while (cur) { if (cur->_key < key) { par = cur; cur = cur->_right; } else if (cur->_key > key) { par = cur; cur = cur->_left; } else { if (cur->_left == nullptr) { if (cur == _root) { _root = cur->_right; } else { if (par->_left == cur) { par->_left = cur->_right; } else { par->_right = cur->_right; } } delete cur; } else if (cur->_right == nullptr) { if (cur == _root) { _root = cur->_left; } else { if (par->_left == cur) { par->_left = cur->_left; } else { par->_right = cur->_left; } } delete cur; } else { node* rp = cur; node* r = cur->_right; while (r->_left) { rp = r; r = r->_left; } cur->_key = r->_key; cur->_val = r->_val; if (rp->_left == r) { rp->_left = r->_right; } else { rp->_right = r->_right; } delete r; } return 1; } } return 0; } ~bstree() { destroy(_root); _root = nullptr; } private: node* copy(node* root) { if (root == nullptr)return nullptr; node* newroot = new node(root->_key, root->_val); newroot->_left = copy(root->_left); newroot->_right = copy(root->_right); return newroot; } void _inorder(node* root) { if (root == nullptr)return; _inorder(root->_left); std::cout << root->_key << ":" << root->_val << " "; _inorder(root->_right); } void destroy(node*root) { if (root==nullptr) { return; } destroy(root->_left); destroy(root->_right); delete root; root = nullptr; } node* _root = nullptr; }; }
版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/4/14 23:51:18

知识图谱构建

知识图谱知识抽取知识融合

作者头像 李华
网站建设 2026/4/15 14:53:47

当下,官网为什么越来越重要了

一、官网革命&#xff1a;AI时代从“展示页”升级到“站点即智能体” AI 大模型的信息采集逻辑&#xff0c;彻底改写了官网的价值坐标系&#xff0c;不再是单纯的企业名片&#xff0c;而是成为“站点即智能体&#xff08;SaaaS, Site As an AI Service&#xff09;”的营销枢纽…

作者头像 李华
网站建设 2026/4/14 6:56:39

BAS模拟入侵攻击系统:前置防控核心,守护企业网络安全

数字化时代&#xff0c;网络威胁持续升级&#xff0c;黑客攻击手段愈发隐蔽、精准&#xff0c;企业数据泄露、系统瘫痪、业务中断等安全事件频发&#xff0c;给企业带来巨额经济损失与品牌声誉损害。在此背景下&#xff0c;被动防御已难以抵御多变的网络威胁&#xff0c;主动探…

作者头像 李华
网站建设 2026/4/12 19:25:57

如何优化微信个人号的API二次开发流程?

在私域运营成为企业增长核心引擎的今天&#xff0c;微信生态的开发却依然让许多团队望而却步——复杂的接口、繁琐的调试、高昂的开发成本。现在&#xff0c;这一切都将改变。GeWe开放平台&#xff1a;微信生态开发的终极解决方案GeWe不是又一个微信API封装工具&#xff0c;而是…

作者头像 李华
网站建设 2026/4/12 15:14:43

C++高并发编程核心技能解析

在当今的多核处理器时代&#xff0c;高并发编程已成为C开发者必须掌握的核心技能。无论是构建高性能服务器、实时交易系统&#xff0c;还是大规模数据处理平台&#xff0c;并发编程能力直接决定了程序的性能和响应能力。本文将深入探讨C高并发编程必须掌握的关键技能和技术栈。…

作者头像 李华
网站建设 2026/4/12 20:37:55

代码重构艺术

代码重构的核心原则保持功能不变的前提下改善代码结构&#xff0c;提高可读性、可维护性和可扩展性。重构不是添加新功能&#xff0c;而是优化现有代码。识别重构时机重复代码超过三处时应考虑提取公共方法。长方法&#xff08;通常超过20行&#xff09;需要拆分为更小的单元。…

作者头像 李华