定义节点
节点的定义比较简单,需定义好 索引key,左右子节点,父节点以及用户想要保存的自定义数据
template <typename T>
class Node{
public:
Node(int key,T t,Node* left,Node *right,Node *parent){
this->t = t;
this->key = key;
this->left = left;
this->right = right;
this->parent = parent;
};
Node *left;
Node *right;
Node *parent;
int key;
//定义自己的数据结构
T t;
};
创建树
就是自己定义一个根起点就好
Node<string> *root;//根节点
节点插入
节点插入比较简单,按照二叉搜索树的定义进行处理就行,索引(key)小的放左边,大的放右边,代码接口(具体实现可参考后面提供的源文件)
bool BinarySearchTree::insert(int key);
节点删除
节点的删除复杂一些,之所以复杂也是为了在做了删除操作后,此树依然为一棵二叉搜索树
其实也就是要处理的情况比较多而已,每一种列出来单独处理,注意细节处理, 就没问题了,
情况一:被删除的节点(红框中代表被删除的节点) 没有子节点,直接删除
情况二:被删除的节点(红框中代表被删除的节点)只有一个子节点(左子节点或右子节点),将被删除的节点的子节点连接到父节点,
情况三:被删除的节点有2个子节点时,就不能直接删除了,需要做以下几步:
- 找到一个可直接删除的节点,也就是前2种情况
- 将找到的节点与当前节点交换位置
- 删除节点
- 注意必须保证二叉树搜索树的性质
为了保证二叉树的性质(小的在左子节点,大的在右子节点),当前的方法就是找到“前驱节点”或“后继节点”来交换位置。 - “前驱节点”:左子树最大节点
- “后继节点”:右子树的最小节点
简单证明,当前节点与前驱节点或后继节点交换位置后,不改变二叉搜索树性质
如下图所示 “A、C、F”都是单个节点,“B、D ”是一个节点集合代表中间所有的节点。
根据前驱节点的特点和二叉树的性质:
已知B<A 同时 B<C,那么将A和C调换位置,B集合中任何节点 依然都小于A或C
同理
已知D>A 同时 D>F,那么将A和F调换位置,D集合中任何节点 依然大于A或F
实际删除时,只需要找一种节点就行了,在这里我使用的是后继节点,现在假设已经找到后继节点了,简单起见,将后继节点分为以下4种情况
- 后继节点为删除节点的右子节点------后继节点 没有子节点,则可以直接删除
- 后继节点为删除节点的右子节点------后继节点 有子节点(只会是单子节点而且是右子节点),则将子节点与父节点连接
- 后继节点不是删除节点的右子节点------后继节点 没有子节点,则可以直接删除
- 后继节点不是删除节点的右子节点------后继节点 有子节点(只会是单子节点而且是右子节点),则将子节点与父节点连接
下图代表了后继节点与被删节点存在的几种位置分布,红框代表要删除的节点,绿框代表后继节点,注意后继节点一定没有左子节点,右子节点可以是单个节点 也可以是树
代码实现
整个二叉搜索树的类文件,只包含了增加和删除操作,其中有qt的打印,注释即可
头文件
#ifndef BINARYSEARCHTREE_H
#define BINARYSEARCHTREE_H
#include <QObject>
template <typename T>
class Node{
public:
Node(int key,T t,Node* left,Node *right,Node *parent){
this->t = t;
this->key = key;
this->left = left;
this->right = right;
this->parent = parent;
};
Node(){};
Node(Node &node){
this->t = node.t;
this->key = node.key;
this->left = node.left;
this->right = node.right;
this->parent = node.parent;
};
Node *left;
Node *right;
Node *parent;
int key;
//定义自己的数据结构
T t;
};
using namespace std;
//template <typename T>
class BinarySearchTree
{
public:
BinarySearchTree();
bool insert(int key);
bool insert(Node<string>* &n);
bool remove(int key);
bool update(int key);
//private:
Node<string> *root;
int treedepth;
};
#endif // BINARYSEARCHTREE_H
源文件
#include "binarysearchtree.h"
#include <QDebug>
BinarySearchTree::BinarySearchTree():root(nullptr)
{
treedepth = 0;
}
//默认需要保存的数据值是与key一样的字符串,
bool BinarySearchTree::insert(int key)
{
if(root == nullptr){
root = new Node<string>(key,"",nullptr,nullptr,nullptr);
}else{
Node<string> * curnode = root;
int level = 0;
while (curnode != nullptr) {
int curkey = curnode->key;
level++;
if(curkey < key){
if(curnode->right == nullptr){
Node<string> *newnode = new Node<string>(key,"",nullptr,nullptr,nullptr);
curnode->right = newnode;
newnode->parent = curnode;
if(treedepth < level)
treedepth = level;
qDebug()<<"插入成功,右值:"<<key<<",深度:"<<treedepth;
break;
}else
curnode = curnode->right;
}else if(curkey > key){
if(curnode->left == nullptr){
if(treedepth < level)
treedepth = level;
Node<string> *newnode = new Node<string>(key,"",nullptr,nullptr,nullptr);
curnode->left = newnode;
newnode->parent = curnode;
qDebug()<<"插入成功,左值:"<<key<<",深度:"<<treedepth;
break;
}else
curnode = curnode->left;
}else{
//插入相同输入
qDebug()<<"插入失败:"<<key;
return false;
}
}
}
return true;
}
bool BinarySearchTree::insert(Node<string>* &n)
{
}
bool BinarySearchTree::remove(int key){
qDebug()<<"删除节点:"<<key;
//找到要删除的节点
Node<string> * curnode = root;
if(root->key == key){
qDebug()<<"不能删除根节点:";
return false;
}
while (curnode != nullptr) {
qDebug()<<"删除节点:"<<curnode->key;
int curkey = curnode->key;
if(curkey < key){
curnode = curnode->right;
}else if(curkey > key){
curnode = curnode->left;
}else{
//子节点都为空,直接删除
if(curnode->left == nullptr && curnode->right == nullptr)
{
qDebug()<<"左右节点都为空";
if(curkey < curnode->parent->key){
curnode->parent->left = nullptr;
qDebug()<<"删除左节点成功:"<<key;
}else if(curkey > curnode->parent->key){
curnode->parent->right = nullptr;
qDebug()<<"删除右节点成功:"<<key;
}
delete curnode;
curnode = nullptr;
break;
}
else if(curnode->left != nullptr && curnode->right == nullptr)
{
/*
* 有且只有一个子节点时,可以将子节点替换到当前节点的位置,然后删除
* 每一个节点都是按搜索二叉树(右子树大,左子树小)的规则插入的,所以在这种情况下子节点替换当前节点,也会满足二叉搜索树的规则
*
* 有一个左子节点,右节点空
*/
if(curkey < curnode->parent->key){
curnode->parent->left = curnode->left;
curnode->left->parent = curnode->parent;
qDebug()<<"删除左节点成功:"<<key;
}
if(curkey > curnode->parent->key){
curnode->parent->right = curnode->left;
curnode->left->parent = curnode->parent;
qDebug()<<"删除右节点成功:"<<key;
}
delete curnode;
curnode = nullptr;
break;
}else if(curnode->left == nullptr && curnode->right != nullptr)
{//有一个右子节点,左节点空
if(curkey < curnode->parent->key){
curnode->parent->left = curnode->right;
curnode->right->parent = curnode->parent;
qDebug()<<"删除左节点成功:"<<key;
}
if(curkey > curnode->parent->key){
curnode->parent->right = curnode->right;
curnode->right->parent = curnode->parent;
qDebug()<<"删除右节点成功:"<<key;
}
delete curnode;
curnode = nullptr;
break;
}else if(curnode->left != nullptr && curnode->right != nullptr){
/*
* 有2个子节点时,
* 1、需要先找到其后继结点(右子树中最小的子节点),找前驱节点(左子树中最大的子节点)也是可以的?
* 2、将后继节点与当前节点替换, 然后删除当前节点
*/
Node<string> *successorNode = curnode->right;
while (successorNode->left != nullptr ) {
successorNode = successorNode->left;
}
qDebug()<<"后继节点:"<<successorNode->key;
//只交换节点的key和值
Node<string> tmpnode(*successorNode);
curnode->key = successorNode->key;
curnode->t = successorNode->t;
successorNode->key = tmpnode.key;
successorNode->t = tmpnode.t;
//后继节点是当前节点的右节点
if(curnode->right->key == successorNode->key){
if(successorNode->right == nullptr){
curnode->right = nullptr;
delete successorNode;
}else{
curnode->right = successorNode->right;
successorNode->right->parent = curnode;
delete successorNode;
}
}else{
if(successorNode->right == nullptr){
successorNode->parent->left = nullptr;
delete successorNode;
}else{
successorNode->parent->left = successorNode->right;
successorNode->right->parent = successorNode->parent;
delete successorNode;
}
}
break;
}
}
}
}
bool BinarySearchTree::update(int key){
}
二叉树实现与显示qt工程链接
qt create打开就行,我的qt版本是5.14
二叉搜索树的实现及显示