news 2026/5/12 2:45:01

【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/5/11 0:11:00

网络编程基础:OSI 模型与 TCP/IP 协议栈详解

作为网络编程的入门核心&#xff0c;理解网络分层模型是掌握数据通信逻辑的关键。本文将拆解 OSI 七层模型的功能&#xff0c;并对比 TCP/IP 协议栈的简化设计&#xff0c;帮你快速建立网络通信的底层认知。一、OSI 七层模型&#xff1a;网络通信的 “标准框架”OSI&#xff08…

作者头像 李华
网站建设 2026/5/3 6:29:51

EagleTrader交易员采访|不遵守交易规则,真的是自由吗?

对很多交易员来说&#xff0c;真正的困难&#xff0c;从来不是行情本身。而是在一次次盈利与回撤之间&#xff0c;仍然愿意相信规则&#xff1b;是在没有掌声、没有监督的交易日里&#xff0c;持续做出那些“看起来不那么刺激”的选择。这类情况也往往只会被少数人长期坚持。朱…

作者头像 李华
网站建设 2026/5/3 8:50:38

java计算机毕业设计鲜花商城网 基于SpringBoot的线上花店零售与配送平台 面向节日场景的O2O鲜花订购与会员营销系统

计算机毕业设计鲜花商城网9&#xff08;配套有源码 程序 mysql数据库 论文&#xff09; 本套源码可以在文本联xi,先看具体系统功能演示视频领取&#xff0c;可分享源码参考。情人节、母亲节、毕业季……“送花”永远是最保险的表达&#xff0c;却也是最耗时的线下跑腿&#xff…

作者头像 李华
网站建设 2026/5/1 2:12:33

注意!教你选出合肥市面上正规又靠谱的门头设计安装企业!

注意&#xff01;教你选出合肥市面上正规又靠谱的门头设计安装企业&#xff01;在商业竞争日益激烈的当下&#xff0c;一个独特且合适的门头设计安装对于企业的品牌形象塑造至关重要。然而&#xff0c;市面上的门头设计安装企业众多&#xff0c;质量参差不齐&#xff0c;如何选…

作者头像 李华
网站建设 2026/5/9 12:07:52

失业 3 个月投 127 份简历?网安零成本转行月薪 12K,你们敢试吗?

失业 3 个月投了 127 份简历&#xff1f;别卷了&#xff01;我靠网安转行月薪 12K&#xff0c;附 3 个月零成本入门攻略 去年被裁那天&#xff0c;我盯着招聘软件上 “35 岁以下优先” 的字样&#xff0c;把简历里的 “5 年行政经验” 改了又改&#xff0c;结果投出去的 127 份…

作者头像 李华
网站建设 2026/5/11 15:03:16

93 年 32 岁 IT 运维失业了!甲方不续约项目解散,你们有同款经历吗?

以上是某红书平台网友分享的真实案例&#xff01; 这两年&#xff0c;IT行业面临经济周期波动与AI产业结构调整的双重压力&#xff0c;确实有很多运维与网络工程师因企业缩编或技术迭代而暂时失业。 很多人都在提运维网工失业后就只能去跑滴滴送外卖了&#xff0c;但我想分享…

作者头像 李华