目录
1、二叉排序树
1.1二叉树排序树插入
1.1.1两种插入方法
1.1.2循环法
1.1.3递归法
1.2二叉树的打印
1.3二叉树的结点删除
1.4销毁二叉树
1.5层次打印
谢谢帅气美丽且优秀的你看完我的文章还要点赞、收藏加关注
没错,说的就是你,不用再怀疑!!!
希望我的文章内容能对你有帮助,一起努力吧!!!
1、二叉排序树
二叉树排序树:以某个结点为准,该结点的左子树内所有的结点都会比该结点要小。该结点的右子树内 的所有结点都会大于该结点。
1.1二叉树排序树插入
- 插入结点
- 树为空的时候
- 把结点作为根结点插入
- 树不为空时候
- 和根结点比较
- 如果比根结点小,则判断根结点左子结点是否存在
- 如果左子结点存在的话,将根结点的左子结点作为根结点继续比较
- 如果不存在,直接将新数据作为该根结点的左子结点插入
- 如果比根结点大,则判断根结点右子结点是否存在
- 如果右子结点存在的话,将根结点的右子结点作为根结点继续比较
- 如果不存在,直接将新数据作为该根结点的右子结点插入
- 如果比根结点小,则判断根结点左子结点是否存在
- 插入新节点无法就是重复上述步骤
- 和根结点比较
- 树为空的时候
1.1.1两种插入方法
1.1.2循环法
- 逻辑
- 如果根结点为空,那么新结点直接作为根结点返回
- 如果不为空
- 通过前后指针,循环移动来找到待插入的位置
- 循环结束之后,待插入的位置
- 父结点指针的下一级
- 需要通过再对父结点数据比较一次,来确定插入方向(左右)
- 父结点指针的下一级
1.1.3递归法
1.2二叉树的打印
- 先序访问
- 先访问根结点,再访问左子树,最后访问右子树(根左右)
- 中序访问
- 先访问左子树,再访问根结点,最后访问右子树(左根右)
- 后序访问
- 先访问左子树,再访问右子树,最后访问根结点(左右根)
示例代码:
#include <iostream>
// 树结点类型
typedef struct treenode
{
int value;
struct treenode *lchild;
struct treenode *rchild;
}Tree;
/*
@brief 增加新的结点进入二叉树中(循环法)
@param tree 需要增加结点的树的根结点指针
@param data 需要增加进树的新结点数据
@return 返回树的根结点
*/
Tree *addNewNodeLoop(Tree *tree,int data)
{
// 如果树为空作为根结点插入
if(tree == nullptr)
{
Tree *node_ptr = new Tree;
node_ptr->value = data;
node_ptr->lchild = nullptr;
node_ptr->rchild = nullptr;
return node_ptr;
}
// 如果树不为空的时候,需要和根结点进行比较
Tree *father = nullptr; // 父结点
Tree *current_node = tree; // 当前结点
while(current_node)
{
father = current_node; // 将当前结点作为下一个结点的父结点
// 当前结点的数据大于data,需要往左查
if(current_node->value > data)
current_node = current_node->lchild; // 将当前结点的左子结点作为新的根结点来继续下次比较
// 当前结点的数据小于data,需要往右查
else if(current_node->value < data)
current_node = current_node->rchild; // 将当前结点的右子结点作为新的根结点来继续下次比较
else
return tree; // 不能存在相等情况,所以直接返回
}
// 出来循环:找到了插入的位置
current_node = new Tree;
current_node->value = data;
current_node->lchild = nullptr;
current_node->rchild = nullptr;
// 确定是左插还是右插
if(father->value > data)
// 左插
father->lchild = current_node;
else
// 右插
father->rchild = current_node;
return tree;
}
/*
@brief 增加新的结点进入二叉树中(递归法)
@param tree 需要增加结点的树的根结点指针
@param data 需要增加进树的新结点数据
@return 返回树的根结点
*/
Tree *addNewNode(Tree *tree,int data)
{
// 如果树为空作为根结点插入
if(tree == nullptr)
{
Tree *node_ptr = new Tree;
node_ptr->value = data;
node_ptr->lchild = nullptr;
node_ptr->rchild = nullptr;
return node_ptr;
}
// 如果树不为空的时候,需要和根结点进行比较
if(tree->value > data)
// 左插
tree->lchild = addNewNode(tree->lchild,data);
else if(tree->value < data)
// 右插
tree->rchild = addNewNode(tree->rchild,data);
return tree;
}
/*
@brief 创建一棵二叉排序树
@return 成功返回创建好的树的首地址
*/
Tree *createNewTree()
{
// 一棵空树
Tree *tree = nullptr;
while(1)
{
int data = -1;
std::cin >> data;
if(data == -1)
break;
// 插入到树中
tree = addNewNode(tree,data);
}
// 返回创建好的树
return tree;
}
/*
@brief 先序遍历二叉树的结点 根左右
@param tree 需要先序遍历的二叉树根结点指针
*/
void frontPrintTree(Tree *tree)
{
// 判断一下根结点是否为空
if(tree == nullptr)
return;
// 把传入的结点直接作为根结点使用
// 打印根结点
std::cout << tree->value;
// 打印左子树:这里的tree->lchild其实就是左子树的根
frontPrintTree(tree->lchild);
// 打印右子树:这里的tree->rchild其实就是右子树的根
frontPrintTree(tree->rchild);
}
/*
@brief 中序遍历二叉树的结点 左根右
@param tree 需要先序遍历的二叉树根结点指针
*/
void middlePrintTree(Tree *tree)
{
// 判断一下根结点是否为空
if(tree == nullptr)
return;
// 把传入的结点直接作为根结点使用
// 打印左子树:这里的tree->lchild其实就是左子树的根
middlePrintTree(tree->lchild);
// 打印根结点
std::cout << tree->value;
// 打印右子树:这里的tree->rchild其实就是右子树的根
middlePrintTree(tree->rchild);
}
/*
@brief 后序遍历二叉树的结点 左右根
@param tree 需要先序遍历的二叉树根结点指针
*/
void backPrintTree(Tree *tree)
{
// 判断一下根结点是否为空
if(tree == nullptr)
return;
// 把传入的结点直接作为根结点使用
// 打印左子树:这里的tree->lchild其实就是左子树的根
backPrintTree(tree->lchild);
// 打印右子树:这里的tree->rchild其实就是右子树的根
backPrintTree(tree->rchild);
// 打印根结点
std::cout << tree->value;
}
int main()
{
// 创建一棵树
Tree * tree = createNewTree();
// 先序后序中序的打印
frontPrintTree(tree);
std::cout << std::endl;
middlePrintTree(tree);
std::cout << std::endl;
backPrintTree(tree);
std::cout << std::endl;
return 0;
}
1.3二叉树的结点删除
- 删除一个结点
- 第一种情况:被删除的结点是叶子结点,直接删除
- 释放叶子结点空间
- 返回空指针
- 第二种情况:被删除结点只有左子树,将左子树中最大的结点替换为需要删除的结点
- 如果左子树中的第一个结点为最大结点(表示该左子树是没有右子树的)
- 将被删除结点的 lchild 指向最大的那个结点的 lchild
- 释放左子树中的第一个结点的空间
- 返回被删除节点
- 如果左子树中第一个节点不为最大结点(表示该左子树是有右子树)
- 将最大结点的父结点的 rchild 指向最大结点的 lchild
- 释放左子树中最大结点的空间
- 返回被删除结点
- 如果左子树中的第一个结点为最大结点(表示该左子树是没有右子树的)
- 第三种情况:被删除的结点只有右子树,将右子树中的最小结点替换为需要删除的结点
- 如果右子树的第一个结点不为最小结点
- 将最小结点的父结点的 lchild 指向最小结点的 rchild
- 释放最小结点
- 返回被删除结点
- 如果右子树的第一个结点为最小结点
- 将被删除结点的 rchild 指向最小结点的 rchild
- 释放最小结点
- 返回被删除结点
- 如果右子树的第一个结点不为最小结点
- 第四种情况:被删除结点即存在左子树也存在右子树
- 可以在右子树中找最小的结点替换,操作和第三种情况方式一样
- 可以在左子树中找最大的结点替换,操作和第二种情况方式一样
- 第一种情况:被删除的结点是叶子结点,直接删除
1.4销毁二叉树
1.5层次打印
- 1、先入队根结点
- 2、出队结点
- 判断当前出队的结点左子树是否存在
- 如果存在将左子树入队
- 判断当前出队的结点右子树是否存在
- 如果存在右子树入队
- 判断当前出队的结点左子树是否存在
- 3、打印结点数据
- 重复以上步骤直到队列为空
***二叉树排序树示例代码***
#include <iostream>
#include <queue>
using std::queue;
template <typename T>
class sort_tree
{
private:
typedef struct tree_node
{
T data;
struct tree_node *lchild;
struct tree_node *rchild;
}TreeNode;
TreeNode *root;
public:
// 默认构造
sort_tree()
: root(nullptr)
{}
// 拷贝构造
sort_tree(const sort_tree &object)
{
}
// 带参构造
sort_tree(std::initializer_list<T> list)
: root(nullptr) // root 如果不置空,那么就是一个野指针
{
// 遍历list列表
for(auto var : list)
add(var); // 通过add来增加结点进树
}
// 先序打印
void frontPrint(TreeNode *tn = nullptr)
{
TreeNode *tree_node =nullptr;
if(tn == nullptr)
tree_node = root;
else
tree_node = tn;
if(tree_node == nullptr)
return;
std::cout << tree_node->data << " ";
if(tree_node->lchild)
frontPrint(tree_node->lchild);
if(tree_node->rchild)
frontPrint(tree_node->rchild);
}
// 层次打印
void levelPrint()
{
if(root == nullptr)
return;
queue<TreeNode *> q;
// 先入根结点
q.push(root);
while(!q.empty())
{
// 获取队头元素
TreeNode *node_ptr = q.front();
// 出队元素
q.pop();
// 打印元素
std::cout << node_ptr->data << " ";
// 判断左右子树是否存在
if(node_ptr->lchild)
q.push(node_ptr->lchild);
if(node_ptr->rchild)
q.push(node_ptr->rchild);
}
std::cout << std::endl;
}
// 销毁删除
TreeNode *destory(TreeNode *tn = nullptr)
{
TreeNode *tree_node = nullptr;
if(tn == nullptr)
tree_node = root;
else
tree_node = tn;
if(tree_node->lchild)
// 通过回溯去更新子树指针指向
tree_node->lchild = destory(tree_node->lchild);
if(tree_node->rchild)
// 通过回溯去更新子树指针指向
tree_node->rchild = destory(tree_node->rchild);
// 根就是需要删除
std::cout << "我被删除了" << tree_node->data << std::endl;
delete tree_node;
return nullptr;
}
// 析构/销毁
~sort_tree()
{
// 从叶子结点开始删除
// 左右根进行遍历销毁
destory();
}
// 增加结点
bool add(T &data)
{
// 判断是不是空树
if(root == nullptr)
{
root = new TreeNode;
root->data = data;
root->lchild = nullptr;
root->rchild = nullptr;
return true;
}
// 如果不为空,查找挂载的位置
TreeNode *father_node = root;
do
{
if(father_node->data > data)
{
// 判断左子结点存不存在
// 如果存在将左子结点设为新的父结点
if(father_node->lchild)
father_node = father_node->lchild;
else // 如果不存在就挂载到father结点的左边
{
father_node->lchild = new TreeNode;
father_node->lchild->data = data;
father_node->lchild->lchild = nullptr;
father_node->lchild->rchild = nullptr;
return true;
}
}
else if(father_node->data < data)
{
// 判断右子结点存不存在
// 如果存在将右子结点设为新的父结点
if(father_node->rchild)
father_node = father_node->rchild;
else // 如果不存在就挂载到father结点的右边
{
father_node->rchild = new TreeNode;
father_node->rchild->data = data;
father_node->rchild->lchild = nullptr;
father_node->rchild->rchild = nullptr;
return true;
}
}
else
return false;
} while (1);
}
// 删除结点
bool del(T &data)
{
// 是不是空树
if(root == nullptr)
return false;
// 不是空树
// 找需要删除的结点
TreeNode *father_ptr = nullptr;
TreeNode *delNode_ptr = root;
do
{
// 如果小于当前结点,那么说明被删除的结点在树的左边
if(delNode_ptr->data > data)
{
// 更新父结点指针
father_ptr = delNode_ptr;
delNode_ptr = delNode_ptr->lchild;
}
// 如果大于当前结点,那么说明被删除的结点在树的右边
else if(delNode_ptr->data < data)
{
// 更新父结点指针
father_ptr = delNode_ptr;
delNode_ptr = delNode_ptr->rchild;
}
else
break; // 找到了需要删除的结点
// 为空表示树中没有找到删除的结点
if(delNode_ptr == nullptr)
return false;
} while (1);
// 能来到这位置。说明找到了需要删除的结点
// 第一种情况:叶子结点
if(!delNode_ptr->lchild&&!delNode_ptr->rchild)
{
// 改变父结点中子树指针的指向。避免父结点中的子树指针成为野指针
if(father_ptr->data > data)
father_ptr->lchild = nullptr;
else if(father_ptr->data < data)
father_ptr->rchild = nullptr;
// 删除结点,释放空间
delete delNode_ptr;
delNode_ptr = nullptr;
return true;
}
// 存在左子树:包含了第四种情况
else if(delNode_ptr->lchild)
{
// 找左子树中最大的结点
TreeNode *maxFather_ptr = nullptr;
TreeNode *maxNode_ptr = delNode_ptr->lchild;
// 一路向右
while(maxNode_ptr->rchild)
{
maxFather_ptr = maxNode_ptr;
maxNode_ptr = maxNode_ptr->rchild;
}
// 第一种情况:被删除结点的lchild指向左子树种最大结点的lchild
if(maxNode_ptr == delNode_ptr->lchild)
delNode_ptr->lchild = maxNode_ptr->lchild;
else
maxFather_ptr->rchild = maxNode_ptr->lchild;
// 值交换
std::swap(delNode_ptr->data,maxNode_ptr->data);
// 删除左子树种最大结点
maxNode_ptr->lchild = nullptr;
delete maxNode_ptr;
maxNode_ptr = nullptr;
return true;
}
// 只有右子树不为空
else
{
// 找右子树中最小的结点
TreeNode *minFather_ptr = nullptr;
TreeNode *minNode_ptr = delNode_ptr->rchild;
// 一路向左
while(minNode_ptr->lchild)
{
minFather_ptr = minNode_ptr;
minNode_ptr = minNode_ptr->lchild;
}
// 第一种情况:被删除结点的rchild指向右子树种最小结点的rchild
if(minNode_ptr == delNode_ptr->rchild)
delNode_ptr->rchild = minNode_ptr->rchild;
else
minFather_ptr->lchild = minNode_ptr->rchild;
// 值交换
std::swap(delNode_ptr->data,minNode_ptr->data);
// 删除右子树种最小结点
minNode_ptr->rchild = nullptr;
delete minNode_ptr;
minNode_ptr = nullptr;
return true;
}
}
};
int main()
{
sort_tree<int> tree({3,1,5,6,4,2,8,9,7});
// do
// {
// int data = -1;
// std::cin >> data;
// if(data == -1)
// break;
// tree.add(data);
// } while (1);
tree.levelPrint();
return 0;
}