C++二叉树进阶——二叉搜索树

二叉搜索树

  • 1. 二叉树的概念
  • 2. 二叉树的实现
    • 2.1创建节点类
    • 2.2 查找Find
    • 2.3 插入Insert
    • 2.4 删除Erase
    • 2.5 中序遍历
    • 2.6 构造/析构
  • 3. 递归实现
    • 3.1 查找FindR
    • 3.2 插入InsertR
    • 3.3 删除EraseR
  • 4.整体代码

1. 二叉树的概念

二叉搜索树又称二叉排序树,它或者是一棵空树,或者是具有以下性质的二叉树:

  • 若它的左子树不为空,则左子树上所有节点的值都小于根节点的值
  • 若它的右子树不为空,则右子树上所有节点的值都大于根节点的值
  • 它的左右子树也分别为二叉搜索树

2. 二叉树的实现

2.1创建节点类

template <class T>
struct BSTreeNode
{typedef BSTreeNode Node;Node* _left;Node* _right;T _key;BSTreeNode(const T& val):_left(nullptr),_right(nullptr),_key(val){}
};

2.2 查找Find

  • 查找就很简单了,因为搜索二叉树的结构特殊,任何一个节点的左节点都小于父节点,任何一个节点的右节点都大于父节点。所以搜索一个值只需要判断大于这个节点还是小于这个节点。如果小于则往左节点走,如果大于则往右节点走。
bool Find(const T& val)
{Node* cur = _root;while (cur){if (val > cur->_key) cur = cur->_right;else if (val < cur->_key) cur = cur->_left;else return true;}return false;
}

2.3 插入Insert

  • 插入也是同样的道理,大于某个节点就往右走,小于某个节点就往左走。直到为空,这个节点就是我们要插入的点。但是因为树是单向的,所以同样要有个parent指针用来记录上一个节点,便于链接节点。

注意细节:

  1. 当插入的节点是第一个节点的时候。
  2. 链接的时候是连接左节点还是右节点。
bool Insert(const T& val)
{//记录链接的节点。Node* parent = nullptr;Node* cur = _root;//当插入的节点是第一个节点时if (_root == nullptr){_root = new Node(val);return true;}while (cur){if (val > cur->_key){parent = cur;cur = cur->_right;}else if (val < cur->_key){parent = cur;cur = cur->_left;}else{return false;}}cur = new Node(val);//判断是链接右节点还是左节点。if (parent->_key < val)parent->_right = cur;elseparent->_left = cur;return true;
}

2.4 删除Erase

删除分三种情况:

  1. 要删除的节点的左节点为空。
  2. 要删除的节点的右节点为空。
  3. 要删除的左右节点都不为空。

在这里插入图片描述
在这里插入图片描述

bool Erase(const T& val)
{Node* cur = _root;Node* parent = nullptr;while (cur){if (val > cur->_key){parent = cur;cur = cur->_right;}else if (val < cur->_key){parent = cur;cur = cur->_left;}else{if (cur->_left == nullptr){//处理删除头节点的情况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){//处理删除头节点的情况if (cur == _root){_root = cur->_left;}else{if (parent->_left == cur){parent->_left = cur->_left;}else{parent->_right = cur->_left;}delete cur;}}else{//替换法//这里要注意力RightMin不能定义成nullptr,因为如果删除的是头节点就会出现野指针的问题,所以这里要定义成curNode* RightMin = cur;//定义右子树,找到右子树最左节点Node* RightCur = cur->_right;while (RightCur->_left){RightMin = RightCur;RightMin = RightMin->_left;}//交换值swap(RightCur->_key, cur->_key);if (RightMin->_left == RightCur){RightMin -> _left = RightCur->_right;}else{RightMin->_right = RightCur->_right;}delete RightCur;}return true;}}return false;
}

2.5 中序遍历

这里我们会发现中序遍历搜索二叉树得到是一个有序的数列。

//这里说明一下,因为中序是要使用到递归的,也就是要传_root成员变量的。
//但是我们又不能修改_root所以要额外写一个函数来是由递归。
void Inorder()
{_Inorder(_root);cout << endl;
}
private:
void _Inorder(Node* root)
{if (root == nullptr)return;_Inorder(root->_left);cout << root->_key << " ";_Inorder(root->_right);
}

2.6 构造/析构

BSTree(Node* root = nullptr):_root(root)
{}BSTree(const BSTree<T>& b)
{_root = Copy(b._root);
}BSTree<T>& operator=(BSTree<T> b)
{swap(_root, b._root);return *this;
}~BSTree()
{Destroy(_root);
}private:
//前序常见节点
Node* Copy(Node* root)
{if (root == nullptr)return nullptr;Node* newNode = new Node(root->_key);newNode->_left = Copy(root->_left);newNode->_right = Copy(root->_right);return newNode;
}//后序delete节点
void Destroy(Node* root)
{if (root == nullptr)return;Destroy(root->_left);Destroy(root->_right);
}

3. 递归实现

递归版本的整体思路是一样的。

3.1 查找FindR

bool FindR(const T& val)
{return _FindR(val, _root);
}
bool _FindR(const T& val, Node* root)
{if (root == nullptr)return false;if (val > root->_key)return _FindR(val, root->_right);else if (val < root->_key)return _FindR(val, root->_left);elsereturn true;
}

3.2 插入InsertR

在这里插入图片描述

bool InsertR(const T& val)
{return _InsertR(val, _root);
}bool _InsertR(const T& val, Node*& root)
{if (root == nullptr){root = new Node(val);return true;}if (val > root->_key)return _InsertR(val, root->_right);else if (val < root->_key)return _InsertR(val, root->_left);elsereturn false;
}

3.3 删除EraseR

删除也是一样的参数传递Node*& root
但是当删除的节点左右都不为空的时候就可以不用替换法了,只需要将将最左节点和删除的点swap一下,在递归删除节点指向的_right也就是右子树再次递归一下即可。

bool EraseR(const T& val)
{return _EraseR(val, _root);
}bool _EraseR(const T& val, Node*& root)
{if (root == nullptr)return false;if (val > root->_key)return _EraseR(val, root->_right);else if (val < root->_key)return _EraseR(val, root->_left);else{Node* del = root;if (root->_left == nullptr){root = root->_right;}else if (root->_right == nullptr){root = root->_left;}else{Node* cur = root->_right;while (cur->_left){cur = cur->_left;}//交换cur节点和最左节点swap(root->_key, cur->_key);递归右子树return _EraseR(val, root->_right);}delete del;return true;}
}

4.整体代码

#pragma once
#include <iostream>
using namespace std;template <class T>
struct BSTreeNode
{typedef BSTreeNode Node;Node* _left;Node* _right;T _key;BSTreeNode(const T& val):_left(nullptr),_right(nullptr),_key(val){}
};template <class T>
class BSTree
{typedef BSTreeNode<T> Node;
public:BSTree(Node* root = nullptr):_root(root){}BSTree(const BSTree<T>& b){_root = Copy(b._root);}BSTree<T>& operator=(BSTree<T> b){swap(_root, b._root);return *this;}~BSTree(){Destroy(_root);}bool Find(const T& val){Node* cur = _root;while (cur){if (val > cur->_key) cur = cur->_right;else if (val < cur->_key) cur = cur->_left;else return true;}return false;}bool Insert(const T& val){Node* parent = nullptr;Node* cur = _root;if (_root == nullptr){_root = new Node(val);return true;}while (cur){if (val > cur->_key){parent = cur;cur = cur->_right;}else if (val < cur->_key){parent = cur;cur = cur->_left;}else{return false;}}cur = new Node(val);if (parent->_key < val)parent->_right = cur;elseparent->_left = cur;return true;}bool Erase(const T& val){Node* cur = _root;Node* parent = nullptr;while (cur){if (val > cur->_key){parent = cur;cur = cur->_right;}else if (val < cur->_key){parent = cur;cur = cur->_left;}else{if (cur->_left == nullptr){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){if (cur == _root){_root = cur->_left;}else{if (parent->_left == cur){parent->_left = cur->_left;}else{parent->_right = cur->_left;}delete cur;}}else{//替换法Node* RightMin = cur;Node* RightCur = cur->_right;while (RightCur->_left){RightMin = RightCur;RightMin = RightMin->_left;}swap(RightCur->_key, cur->_key);if (RightMin->_left == RightCur){RightMin -> _left = RightCur->_right;}else{RightMin->_right = RightCur->_right;}delete RightCur;}return true;}}return false;}void Inorder(){_Inorder(_root);cout << endl;}///递归版本/bool FindR(const T& val){return _FindR(val, _root);}bool InsertR(const T& val){return _InsertR(val, _root);}bool EraseR(const T& val){return _EraseR(val, _root);}private:bool _EraseR(const T& val, Node*& root){if (root == nullptr)return false;if (val > root->_key)return _EraseR(val, root->_right);else if (val < root->_key)return _EraseR(val, root->_left);else{Node* del = root;if (root->_left == nullptr){root = root->_right;}else if (root->_right == nullptr){root = root->_left;}else{Node* cur = root->_right;while (cur->_left){cur = cur->_left;}swap(root->_key, cur->_key);return _EraseR(val, root->_right);}delete del;return true;}}bool _InsertR(const T& val, Node*& root){if (root == nullptr){root = new Node(val);return true;}if (val > root->_key)return _InsertR(val, root->_right);else if (val < root->_key)return _InsertR(val, root->_left);elsereturn false;}bool _FindR(const T& val, Node* root){if (root == nullptr)return false;if (val > root->_key)return _FindR(val, root->_right);else if (val < root->_key)return _FindR(val, root->_left);elsereturn true;}
private:Node* Copy(Node* root){if (root == nullptr)return nullptr;Node* newNode = new Node(root->_key);newNode->_left = Copy(root->_left);newNode->_right = Copy(root->_right);return newNode;}void Destroy(Node* root){if (root == nullptr)return;Destroy(root->_left);Destroy(root->_right);delete root;}void _Inorder(Node* root){if (root == nullptr)return;_Inorder(root->_left);cout << root->_key << " ";_Inorder(root->_right);}
private:Node* _root = nullptr;
};

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/471523.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

springboot743二手交易平台

springboot743二手交易平台 获取源码——》公主号&#xff1a;计算机专业毕设大全

2024 年 11 款最佳 iPhone 数据恢复软件和应用程序

数据丢失是任何人都无法承受的&#xff0c;因为它对每个人都至关重要。但导致数据丢失的原因有很多&#xff0c;一些常见的原因是意外删除数据、设备被盗、iOS 越狱、硬件损坏、病毒感染等。我们列出了 iOS 的顶级恢复工具&#xff0c;其中包括&#xff1a;将帮助您方便地恢复数…

相机图像质量研究(20)常见问题总结:CMOS期间对成像的影响--全局快门/卷帘快门

系列文章目录 相机图像质量研究(1)Camera成像流程介绍 相机图像质量研究(2)ISP专用平台调优介绍 相机图像质量研究(3)图像质量测试介绍 相机图像质量研究(4)常见问题总结&#xff1a;光学结构对成像的影响--焦距 相机图像质量研究(5)常见问题总结&#xff1a;光学结构对成…

【Java多线程】对进程与线程的理解

目录 1、进程/任务&#xff08;Process/Task&#xff09; 2、进程控制块抽象(PCB Process Control Block) 2.1、PCB重要属性 2.2、PCB中支持进程调度的一些属性 3、 内存分配 —— 内存管理&#xff08;Memory Manage&#xff09; 4、线程&#xff08;Thread&#xff09;…

Go语言的100个错误使用场景(40-47)|字符串函数方法

前言 大家好&#xff0c;这里是白泽。 《Go语言的100个错误以及如何避免》 是最近朋友推荐我阅读的书籍&#xff0c;我初步浏览之后&#xff0c;大为惊喜。就像这书中第一章的标题说到的&#xff1a;“Go: Simple to learn but hard to master”&#xff0c;整本书通过分析100…

如何在Windows中配置多个显示器?这里提供详细步骤

Windows可以通过多种方式使用多个显示器,扩展或复制主显示器。你甚至可以关闭主显示器。以下是如何使用简单的键盘快捷键更改辅助显示设置。 使用Windows+P投影菜单 要快速更改Windows 10处理多个显示器的方式,请按Windows+P。屏幕右侧会弹出一个名为“投影”的深灰色菜单。…

Base64编码的优点与缺点

title: Base64编码的优点与缺点 date: 2024/2/16 14:06:37 updated: 2024/2/16 14:06:37 tags: Base64编码ASCII转换数据传输文本存储安全性数据膨胀字符串解码 Base64编码是一种将二进制数据转换为可打印ASCII字符的编码方式。它被广泛应用于数据传输和存储&#xff0c;以提升…

- 工程实践 - 《QPS百万级的有状态服务实践》01 - 存储选型实践

本文属于专栏《构建工业级QPS百万级服务》 《QPS百万级的无状态服务实践》已经完成。截止目前为止&#xff0c;支持需求“给系统传入两个日期&#xff0c;计算间隔有多少天”的QPS百万级服务架构已经完成。如图1&#xff1a; 图1 可是这个架构不能满足需求“给系统传入两个日期…

node+vue3+mysql前后分离开发范式——实现对数据库表的增删改查

文章目录 ⭐前言⭐ 功能设计与实现💖 node后端操作数据库实现增删改查💖 vue3前端实现增删改查⭐ 效果⭐ 总结⭐ 结束⭐结束⭐前言 大家好,我是yma16,本文分享关于 node+vue3+mysql前后分离开发范式——实现对数据库表的增删改查。 技术选型 前端:vite+vue3+antd 后端:…

用HTML和CSS打造跨年烟花秀视觉盛宴

目录 一、程序代码 二、代码原理 三、运行效果 一、程序代码 <!DOCTYPE html> <html lang"en"><head><meta charset"UTF-8"><title>跨年烟花秀</title><meta name"viewport" content"widthdevi…

【Linux】 Linux 小项目—— 进度条

进度条 基础知识1 \r && \n2 行缓冲区3 函数介绍 进度条实现版本 1代码实现运行效果 版本2 Thanks♪(&#xff65;ω&#xff65;)&#xff89;谢谢阅读&#xff01;&#xff01;&#xff01;下一篇文章见&#xff01;&#xff01;&#xff01; 基础知识 1 \r &&a…

你知道.NET的字符串在内存中是如何存储的吗?

一、字符串对象的内存布局 从“值类型”和“引用类型”来划分&#xff0c;字符串自然属于引用类型的范畴&#xff0c;所以一个字符串对象自然采用引用类型的内存布局。引用类型实例的内存布局总的来说整个内存布局分三块&#xff1a;ObjHeader TypeHandle Payload。对于一般…