主题:
代码实现:二叉排序树(BST)的插入、查找(非递归),递归算法、中序遍历二叉排序树,输出有序树,层序遍历BST树、创建一颗二叉排序树、二叉排序树的删除等等。
概念:
没什么好说的,二叉排序树(BST)就是一个中序遍历有序的树,如图中的红色的区域左边整体小于右边,以77为根,明显绿色区域小于黄色区域,
所以进行插入时,遇到了比它的根节点大的,就放在左边,比它的根节点小的就放在右边,然后递归,如果遇到了合适的空位,也就是tr==NULL
这时候就是插入的时候,
难点:
二叉排序树(BST)最难的地方在于其删除操作,
- 当是需要删除的节点是
叶子节点,可以直接删掉
,因为它没有左右孩子,不需要考虑删除后,二叉排序树的性质改变。 - 当需要删除的节点只有一个孩子时,这个孩子可以是左孩子,也可以是右孩子;需要删除当前节点,然后把左孩子(或者右孩子)接到这个被删除节点原本的位置上,也就是
被删除节点的父节点的对应孩子变成了被删除节点的孩子
,可以理解为【儿子谋权篡位,杀了爸爸,然后继承了爷爷的皇位】 。——————这个从字面上看,需要找到被删除节点的父节点————————而一个巧妙的实现是,交换父节点和儿子节点的值,然后删除儿子节点,并把儿子节点的儿子节点接到原本的父节点的位置。(本文使用的是查找父节点的方式,便于理解) - 当需要删除的节点有两个孩子时,删除该节点时,左边都是比该节点小的值,右边都是比该节点大的值,所以需要找到一个合适的值【接替皇位】,这个值可以是正好比该节点小的值(也就是中序遍历的前驱节点)【皇帝的弟弟】,也可以是正好比该节点大的值【皇帝的哥哥】(也就是中序遍历的,后继节点)。这个值作为根节点,显然,这个根节点左边的值都比根节点小,右边的值都比根节点大。————为了实现这个目标,需要一个查找后继节点的函数,并且需要删除原节点,且把接替它的新的根【新皇帝】节点接上去【顶替皇位】。——————一个巧妙的实现方法同上2:
交换要删除的值和顶替这个被删除的节点值的值;然后删除这个旧值的节点,这样一来,就转化为了(1或者2或者3)
例如:
删除77,87和77交换位置,而删除77的操作是情况2,需要删除的节点只有一个孩子,这样一来,就可以递归地删除,77的孩子顶替77,
得到结果:
输入:
5
77
12
87
99
43
123
0
函数调用:
BSTree_use.cpp
#include "BSTree.h"
int main()
{
int key = 5;
BSTree tr = CreateBSTree();
std::cout << "请输入需要查找的值:";
std::cin >> key;
std::cout << "二叉排序树查找" << key << "(非递归)" << BST_Search(tr, key) << std::endl;
std::cout << "二叉排序树查找" << key << "(递归)" << FindNodeK(tr, key) << std::endl;
DeleteBSTreeNode(tr, 77);
DeleteBSTreeNode(tr, 5);
DeleteBSTreeNode(tr, 12);
DeleteBSTreeNode(tr, 87);
DeleteBSTreeNode(tr, 99);
DeleteBSTreeNode(tr, 123);
DeleteBSTreeNode(tr, 43);
}
函数定义:
BSTree.h
#include <iostream>
#include <stack>
#include <queue>
typedef struct BSNode
{
int data;
struct BSNode *lchild, *rchild;
} * BSTree, BSNode;
/***
* 二叉排序树(BST)的插入
* 成功返回1, 失败返回0
*/
int BSTreeInsert(BSTree &tr, int k)
{
if (!tr)
{
tr = (BSNode *)malloc(sizeof(BSNode));
tr->data = k;
tr->lchild = tr->rchild = NULL;
return 1;
}
else if (k == tr->data)
{ //已经有了这个节点了
return 0;
}
else if (k < tr->data)
{
return BSTreeInsert(tr->lchild, k);
}
else
{
return BSTreeInsert(tr->rchild, k);
}
}
/***
* 二叉排序树的查找(非递归)
*/
BSNode *BST_Search(BSTree tr, int k)
{
while (tr && k != tr->data)
{
if (k < tr->data)
tr = tr->lchild;
else
{
tr = tr->rchild;
}
}
return tr;
}
/***
* 二叉排序树(BST)的查找,递归算法
*/
BSNode *FindNodeK(BSTree tr, int k)
{
if (!tr)
return NULL;
FindNodeK(tr->lchild, k);
if (tr->data == k)
return tr;
FindNodeK(tr->rchild, k);
}
/**
* 中序遍历二叉排序树,输出有序树
*/
void VisitBSTree(BSTree tr)
{
if (tr)
{
VisitBSTree(tr->lchild);
std::cout << tr->data << " ";
VisitBSTree(tr->rchild);
}
}
/**
* 层序遍历BST树
*/
void VisitLevelBSTree(BSTree tr)
{
std::queue<BSNode *> q;
BSNode *p = tr, *r = tr;
q.push(p);
while (!q.empty())
{
p = q.front();
q.pop();
if (p)
{
std::cout << p->data << " ";
q.push(p->lchild);
q.push(p->rchild);
if (p == r)
{
std::cout << std::endl;
r = q.back();
}
}
}
}
/**
* 创建一颗二叉树
*/
BSTree CreateBSTree()
{
int input = 5;
BSTree tr = NULL;
std::cout << "请输入树的节点值,输入0结束: ";
std::cin >> input;
while (input)
{
std::cout << BSTreeInsert(tr, input) << " "
<< "目前层序遍历BST树序列:\n";
VisitLevelBSTree(tr);
std::cout << std::endl;
std::cout << "请输入树的节点值,输入0结束: ";
std::cin >> input;
}
return tr;
}
/***
* 二叉排序树的删除
* 1.如果是叶子节点x,直接删除
* 2.如果x只有左儿子,或者只有右儿子,让子树成为被删除节点x的字数,替代x的位置
* 3.节点有左、右两颗子树,令x的后继节点(或者前驱节点)替代x,
* 然后从二叉树删除这个后记节点(或者前驱节点)
* 所以需要额外写一个查找x的后继节点(或者前驱节点)的方法
*/
/***
* 查找节点x的父亲节点的函数
*/
BSNode *FindNodeFather(BSTree tr, int x)
{
std::stack<BSNode *> s;
BSNode *p = tr;
while (p || !s.empty())
{
if (p)
{
s.push(p);
p = p->lchild;
}
else
{
p = s.top();
s.pop();
if ((p->lchild && p->lchild->data == x) || (p->rchild && p->rchild->data == x))
return p;
p = p->rchild;
}
}
return NULL;
}
/**
* 查找BST二叉树的直接后继
*/
BSNode *FindDirectChild(BSTree tr, int x)
{
int next = 0;
std::stack<BSNode *> s;
BSNode *p = tr;
while (p || !s.empty())
{
if (next && p)
return p;
if (p)
{
s.push(p);
p = p->lchild;
}
else
{
p = s.top();
s.pop();
if (p->data == x)
next = 1;
p = p->rchild;
}
}
return NULL;
}
bool DeleteBSTreeNode(BSTree &tr, int x)
{
if (!tr)
return false; //空树,还删除p =。=
BSNode *p = tr;
std::stack<BSNode *> s;
while (!s.empty() || p)
{
if (p)
{
s.push(p);
p = p->lchild;
}
else
{
p = s.top();
if (p->data == x) //找到了这个节点
{
if (!p->lchild && !p->rchild) //是叶子节点,直接删除
{
std::cout << "删除叶子节点 " << x << std::endl;
BSNode *tmp = FindNodeFather(tr, x);
if (!tmp)
{
//返回了空值,说明没有父节点,这个叶子节点是根节点
tr = NULL;
return true;
}
else
{
if (tmp->lchild && tmp->lchild == p)
tmp->lchild = NULL;
else if (tmp->rchild && tmp->rchild == p)
tmp->rchild = NULL;
else
{
std::cout << "-----代码写错了,删除叶子出现了不可能出现的情况-----";
}
}
free(p);
VisitBSTree(tr);
std::cout << std::endl;
return true;
}
else if (p->lchild && p->rchild)
{
std::cout << "删除双孩子节点 " << x << std::endl;
BSNode *t = FindDirectChild(tr, x);
if (t)
{
std::cout << "找到" << x << "后继节点:" << t->data << std::endl;
int deleteNum = 1000000;
p->data = t->data;
t->data = deleteNum;
DeleteBSTreeNode(tr, deleteNum);
}
else
{
std::cout << "-----代码写错了,删除双孩子出现了不可能出现的情况-----";
}
return false;
}
else if (!p->lchild || !p->rchild)
{
std::cout << "删除单孩子节点 " << x << std::endl;
BSNode *child;
if (p->lchild)
child = p->lchild;
else
child = p->rchild;
BSNode *tmp = FindNodeFather(tr, x);
if (!tmp)
{
//返回了空值,说明没有父节点,这个单孩子节点是根节点
tr = child;
std::cout << "删除根孩子节点,更新根节点为: " << tr->data << std::endl;
}
else
{
if (tmp->lchild && tmp->lchild == p)
tmp->lchild = child;
else if (tmp->rchild && tmp->rchild == p)
tmp->rchild = child;
else
{
std::cout << "-----代码写错了,删除单孩子出现了不可能出现的情况-----";
}
}
free(p);
VisitBSTree(tr);
std::cout << std::endl;
return true;
}
else
{
std::cout << "-----代码写错了,删除出现了不可能出现的情况-----";
}
return false;
}
s.pop();
p = p->rchild;
}
}
return false; //没有这个节点,直接返回了
}
碎碎念
实现大概花了4个小时,比较慢,有些地方实在不能立刻理解,好在最后全部实现了 = = 。