一、简介:
二叉排序树(也称为二叉搜索树或BST)是一种特殊的二叉树,具有以下性质:
- 左子树上的所有节点的值都小于根节点的值。
- 右子树上的所有节点的值都大于根节点的值。
- 左子树和右子树也都是二叉排序树。
根据这些性质,二叉排序树的遍历结果如下:
-
前序遍历(Preorder Traversal):根节点 -> 左子树 -> 右子树。这意味着你首先访问根节点,然后按照前序遍历的顺序依次访问左子树和右子树。前序遍历可以用来复制整个二叉排序树。
-
中序遍历(Inorder Traversal):左子树 -> 根节点 -> 右子树。中序遍历对于二叉排序树来说,会按照升序访问树中的节点值,因此得到的结果是一个按照升序排列的节点值序列。这使得中序遍历在查找、排序等操作中非常有用。
-
后序遍历(Postorder Traversal):左子树 -> 右子树 -> 根节点。后序遍历的结果是从下到上,从叶子节点到根节点的顺序,常用于释放二叉树的内存空间。
这些遍历方式可以根据具体需求来选择。前序遍历通常用于复制树结构,中序遍历用于按顺序访问树中的节点,后序遍历用于释放资源。在二叉排序树中,中序遍历是最常用的,因为它可以按顺序访问节点值,从而实现搜索和排序等操作。
二、实现
关于用C++结构体实现的二叉树:二叉树的构建(C++)_二叉树的建立c++-CSDN博客
这里我们使用类来实现一个功能相对完整的二叉树,这个二叉树是根据值的大小构建的,从小到大分别是左节点,根节点,右节点的插入节点时,所以每次插入新节点时会从根节点开始,跟所有节点的值进行判断大小,最后插入到合适位置。
二叉树节点类:
#pragma once
#include<iostream>
using namespace std;
template<typename T>
class BinaryTree;
template<typename T>
class BinaryTreeNode
{
friend class BinaryTree<T>;
private:
T val;//数据域
BinaryTreeNode<T>* pLeft;//左节点(子树)
BinaryTreeNode<T>* pRight;//右节点(子树)
BinaryTreeNode<T>* pParent;//父节点
public:
BinaryTreeNode(T val, BinaryTreeNode<T>* pLeft = nullptr,BinaryTreeNode<T>* pRight=nullptr,BinaryTreeNode<T>* pParent = nullptr);
~BinaryTreeNode();
public:
//遍历以当前节点为根节点的子树
void preOrder(); //前序遍历
void inOrder();//中序
void postOrder();//后序
int getHeight();//求以当前节点为根节点的树的深度
int getSize();//求以当前节点为根节点的树的节点数量
void insert(const T& val);//插入节点
};
template<typename T>
BinaryTreeNode<T>::BinaryTreeNode(T val, BinaryTreeNode<T>* pLeft, BinaryTreeNode<T>* pRight, BinaryTreeNode<T>* pParent)
:val(val),pLeft(pLeft),pRight(pRight),pParent(pParent){
}
template<typename T>
BinaryTreeNode<T>::~BinaryTreeNode() {
pLeft = nullptr;
pRight = nullptr;
pParent = nullptr;
}
template<typename T>
//遍历以当前节点为根节点的子树
//前序遍历
void BinaryTreeNode<T>::preOrder() {
cout << val << endl;
if (pLeft != nullptr) {
pLeft->preOrder();
}
if (pRight != nullptr) {
pRight->preOrder();
}
}
//中序遍历
template<typename T>
void BinaryTreeNode<T>::inOrder() {
if (pLeft != nullptr) {
pLeft->preOrder();
}
cout << val << endl;
if (pRight != nullptr) {
pRight->preOrder();
}
}
//后序遍历
template<typename T>
void BinaryTreeNode<T>::postOrder() {
if (pLeft != nullptr) {
pLeft->preOrder();
}
if (pRight != nullptr) {
pRight->preOrder();
}
cout << val << endl;
}
//求以当前节点为根节点的树的深度
template<typename T>
int BinaryTreeNode<T>::getHeight() {
//如果pLeft为空返回0,不为空继续查找
int leftDepth = pLeft?pLeft->getHeith():0;
int rightDepth = pRight?pRight->getHeight():0;
return 1 + (leftDepth > rightDepth ? leftDepth : rightDepth);
}
//求以当前节点为根节点的树的节点数量
template<typename T>
int BinaryTreeNode<T>::getSize() {
int leftNum = pLeft ? pLeft->getSize():0;
int rightNum = pRight ? pRight->getSize():0;
return 1 + leftNum + rightNum;
}
//插入节点
template<typename T>
void BinaryTreeNode<T>::insert(const T& val) {
if (val < this->val) {
if (pLeft != nullptr) {
pLeft->insert(val);
}
else {
pLeft = new BinaryTreeNode<T>(val);
return;
}
}
else if (val > this->val) {
if (pRight != nullptr) {
pRight->insert(val);
}
else {
pRight = new BinaryTreeNode<T>(val);
return;
}
}
else //与某个节点相等,不插入
return;
}
二叉树类:
#pragma once
#include "BinaryTreeNode.h"
template<typename T>
class BinaryTree
{
public:
BinaryTree();
~BinaryTree();
private:
//存放根节点
BinaryTreeNode<T> *pRoot;
public:
void insert(const T& val);//插入一个值
void preOrder(); //前序遍历
void inOrder();//中序
void postOrder();//后序
int getHeight();//求以当前节点为根节点的树的深度
int getSize();//求以当前节点为根节点的树的节点数量
void Delete(const T& val);//删除节点
};
template<typename T>
BinaryTree<T>::BinaryTree():pRoot(nullptr)
{
}
template<typename T>
BinaryTree<T>::~BinaryTree()
{
delete pRoot;
pRoot = nullptr;
}
//前序遍历
template<typename T>
void BinaryTree<T>::preOrder() {
pRoot->preOrder();
}
//中序遍历
template<typename T>
void BinaryTree<T>::inOrder() {
pRoot->inOrder();
}
//后序遍历
template<typename T>
void BinaryTree<T>::postOrder() {
pRoot->postOrder();
}
//求以当前节点为根节点的树的深度
template<typename T>
int BinaryTree<T>::getHeight() {
return pRoot->getHeight();
}
//求以当前节点为根节点的树的节点数量
template <typename T>
int BinaryTree<T>::getSize() {
return pRoot->getSize();
}
//插入一个值
template<typename T>
void BinaryTree<T>::insert(const T& val) {
if (pRoot == nullptr) {//如果根节点还为空时,直接新建一个根节点
pRoot= new BinaryTreeNode<T>(val);
}
pRoot->insert(val);
}
//删除节点,删除后要调整二叉树,满足左小右大结构,这个有点麻烦,查了好多资料
template<typename T>
void BinaryTree<T>::Delete(const T& val) {
BinaryTreeNode<T>* pNode = pRoot;
//首先遍历查找要删除的节点
while (pNode != nullptr&&pNode->val != val) {
if (val > pNode->val)
pNode = pNode->pRight;
else if (val < pNode->val)
pNode = pNode->pLeft;
}
//没有找到该节点
if (pNode == nullptr) {
cout << "未找到要删除的节点" << endl;
}
else {//要删除的节点存在,分三种情况
//第一种情况:要删除的是叶子节点
if (pNode->pLeft == nullptr&&pNode->pRight == nullptr) {
delete pNode;
}
//第二种情况:要删除的节点有一个节点,可再细分为只有左节点或者右节点
else if (pNode->pLeft != nullptr&&pNode->pRight == nullptr) {
BinaryTreeNode<T>* pTMP = pNode;
pNode = pNode->pLeft;
delete pTMP;
}
else if (pNode->pLeft == nullptr&&pNode->pRight != nullptr) {
BinaryTreeNode<T>* pTMP = pNode;
pNode = pNode->pRight;
delete pTMP;
}
//第三种情况:要删除的节点既有左节点,又有右节点
else {
//找到一个后继节点值比要删除的节点大,但是最接近要删除的节点,即为要删除的节点的右节点中的最小值
BinaryTreeNode<T>* pTMP = pNode->pRight;
while (pTMP->pLeft != nullptr)
pTMP = pTMP->pLeft;
//将找到的这个后继节点的值跟要删除的节点值交换
//此时有两种情况,这个后继节点有右节点或者没有右节点
//1.如果没有右节点,问题变成整个问题的第一种情况,因为后继节点是个叶子节点,交换后直接删除
if (pTMP->pRight == nullptr) {
pNode->val = pTMP->val;
delete pTMP;
}
//2.如果有右节点,问题变成整个问题的第二种情况
else {
pNode->val = pTMP->val;
BinaryTreeNode<T>* pNodeTMP = pTMP;
pTMP = pTMP->pRight;
delete pNodeTMP;
}
}
}
}