前言
下图代码是排序二叉树的相关操作的实现。众所周知,排序二叉树的优点就是插入高效,查找高效(除顺序序列插入,否则查找数据的时间复杂度都为O(logn),并且中序遍历二叉树的输出结果都是顺序序列。但在删除结点时比较复杂,接下来会对排序二叉树结点的删除做具体分析!
#include<iostream>
#include<time.h>
#include<Windows.h>
using namespace std;
#define NUM 100
#pragma warning(disable:4996)
typedef struct node
{
char data;
struct node* lchild;
struct node* rchild;
}*TNode, Tnode;
void createordtree(TNode &T, char key); //建立排序二叉树
void printtree(TNode T);//中序遍历输出排序二叉树
void findtree(TNode T, char key);//查询排序二叉树
void Findtree(TNode T, char key);//非递归查询排序二叉树
void temp(TNode T, TNode &parent, TNode &child, char key);//寻找排序二叉树删除位置的地址
void deletenode(TNode &T,TNode parent, TNode child);//删除排序二叉树的对应节点
int main()
{
TNode deletep, deletec;
char a, x,find,deletedata;
TNode Tree=NULL;
while (1)
{
cout << "请输入录入的字母信息:";
cin >> a;
createordtree(Tree, a);
cout << "是否继续录入(Y/N)";
cin >> x;
if (x == 'N')break;
}
cout << "中序遍历的结果为:";
printtree(Tree);
cout << endl;
while(1)
{
cout << "请输入需要在排序二叉树中查找的元素:" ;
cin >> find;
cout << "递归查询结果:";
findtree(Tree, find);
cout << "非递归查询结果:";
Findtree(Tree, find);
cout << "是否继续查找(Y/N)";
cin >> x;
if (x == 'N')break;
}
while(1)
{
cout << "请输入需要删除的数据:";
cin >> deletedata;
deletec = Tree;
temp(Tree, deletep, deletec, deletedata);
deletenode(Tree,deletep, deletec);
cout << "删除后重新中序遍历后的结果为:";
printtree(Tree);
cout <<endl<< "是否继续删除(Y/N)";
cin >> x;
if (x == 'N')break;
}
return 0;
}
void createordtree(TNode &T, char key) //建立排序二叉树
{
if (T==NULL)
{
T = new Tnode;
T->data = key;
T->lchild = NULL;
T->rchild = NULL;
}
else if (T->data > key)createordtree(T->lchild, key);
else if (T->data <= key)createordtree(T->rchild, key);
}
void printtree(TNode T)//中序遍历输出排序二叉树
{
if (T)
{
printtree(T->lchild);
cout << T->data;
printtree(T->rchild);
}
}
void findtree(TNode T, char key)//查询排序二叉树
{
if (T == NULL)
{
cout << "NO EXIST!" << endl;
return;
}
else if (T->data == key)
{
cout << "EXIST!" << endl;
return;
}
else if (T->data > key)findtree(T->lchild, key);
else if (T->data < key)findtree(T->rchild, key);
}
void Findtree(TNode T, char key)//非递归查询排序二叉树
{
TNode p = T;
while (p)
{
if (p->data == key)
{
cout << "EXIST!" << endl;
break;
}
else if(p->data > key)p = p->lchild;
else p = p->rchild;
}
if(!p)cout<< "NO EXIST!" << endl;
}
void temp(TNode T,TNode &parent,TNode &child,char key)//寻找排序二叉树删除位置的地址
{
if (T->data == key)
{
parent=T;
child = T;
return;
}
if (child == NULL)
{
cout << "ERROR" << endl;
return;
}
else if(child->data == key)
{
return;
}
else if (child->data > key)
{
parent = child;
child = child->lchild;
temp(T, parent, child, key);
}
else if (child->data < key)
{
parent = child;
child = child->rchild;
temp(T, parent, child, key);
}
}
void deletenode(TNode &T,TNode parent, TNode child)//删除排序二叉树的对应节点
{
TNode p, s;
if (T == child)
{
if (T->lchild == NULL && T->rchild != NULL)
{
T = child->rchild;
delete child;
return;
}
else if (T->rchild == NULL && T->lchild != NULL)
{
T = child->lchild;
delete child;
return;
}
}
if (child->lchild == NULL)
{
if (parent->lchild == child)
{
parent->lchild = child->rchild;
delete child;
}
else
{
parent->rchild = child->rchild;
delete child;
}
}
else if (child->rchild == NULL )
{
if (parent->lchild == child)
{
parent->lchild = child->lchild;
delete child;
}
else
{
parent->rchild = child->lchild;
delete child;
}
}
else if (child->lchild != NULL && child->rchild != NULL)
{
p = child; s = child->lchild;
while (s->rchild != NULL)
{
p = s;
s = s->rchild;
}
child->data = s->data;
if (p != child)
{
p->rchild = s->lchild;
delete s;
}
else
{
p->lchild = s->lchild;
delete s;
}
}
}
结点删除情况总结
一.待删除结点为根结点,且根结点的左子树或右子树为空
解决方案:直接将根结点顺位继承,即根结点=根结点->右孩子/左孩子(如下图情况Ⅵ)
二.待删除结点为子结点,且子结点的左子树/右子树为空
解决方案:寻找删除结点及删除结点的父结点,让父结点指向删除结点的剩余结点(如下图情况Ⅲ)
三.待删除结点的的左子树和右子树都不为空
解决方案:将待删除结点左子树中的最大结点找到,并将其赋值给待删除结点
此时会衍生出两种情况
①待删除结点的左孩子就是最大结点:此时在赋值完成后,直接让待删除结点->左孩子=待删除结点->左孩子->左孩子(如下图情况Ⅴ)
②待删除结点的左孩子不是最大结点:定义两个指针,一个指向最大结点,一个指向最大结点的父结点,在赋值完成后,让最大结点的父结点->右孩子=最大结点->左孩子(如下图情况Ⅱ)
四.待删除的结点为叶子结点
解决方案:直接将此结点删除即可(如下图情况Ⅳ)
图Ⅰ是演示示例的逻辑结构图,其他图是对应结点的删除后的逻辑结构图
所以编译器中的输入应为ADBHCFIEG
结点的删除顺序应为HBFEIDA
下图是对应代码的运行结果