时间:2.5h
难度: 简单偏中等
[反思]我把delete搞得太复杂,同时还忘记了删除时,被删除节点同时具有左右子树可以选择左子树最大或者右子树最小进行填充; 原来只有被删除节点只有一个孩子节点时直接将孩子节点拿来填充,当他就有左儿子又有右儿子时才采取选择策略.并不是在只有右儿子的情况下一定只能拿右儿子中中序遍历的第一个来填充。
Hints:
如何处理 delete() 函数使关键呀!我差点就栽倒这里起不来了!!!
reference:
1.数据结构(七)二叉搜索树_叫我皮卡丘的博客-CSDN博客
[if I were a teacher] 同学们,delete函数有点难搞哦,复杂情况下需要将被删除节点(记为p)的子树里中序遍历的第一个节点拉上来,但具体怎么拉这个节点很重要,需要耐心地去分析!不妨先将被删除的节点分类, 第I类:没有右子树; 第II类:存在右子树(为什么这么分呢?);
我们处理第II类时又会发现由两种情况,
- case1: p(被删除节点)的右子树里按照中序遍历的第一个元素(p_next)就是p的右孩子==> p_next 没有左孩子==> 可以直接将p_next拉上来;
- case2:p(被删除节点)的右子树里按照中序遍历的第一个元素(p_next)不是p的右孩子 ==> p_next 有左孩子 ==> 难搞了---i.e我要放弃了!!! ==>算了,继续学==> 这回引发类似多米诺骨牌效应,要是被删除节点的右子树一直拥有左子树 ==> 一下子想到了堆的'上拉' or '过滤', 我不删除节点,直接将p_next的值取代p的值(做完这一步就与该层的p无任何关系了,任务传递给下一层去完成), 然后在递归地删除 p_next所在地子树中 p_next.
那同学们知道为什么要分这两种情况吗?(case1, case 2)?
A:因为如果将这两种情况视为一种,在处理时会与遇到一个麻烦--case1若使用递归的方法,p_next就为所在子树的树根,这样删除它并不能保证它的父节点能够删除与它的联系(因为递归到了以p_next为根的这一层).或许使用C++的引用能够解决这个问题(说法错误,原来在这里给自己挖坑了,没有利用好递归的'回来'),传pre_Right给下一层递归(pre为p_next的父节点)
[优化] 充分利用递归,第II类里面不用分类可以同时处理啦!
直接递归求解.
/**
* 删除BST中值为X的节点
* 分情况:1.无右孩子 2.存在右孩子(递归调用删除by已被删除节点的父节点做根节点)
*/
BinTree Delete(BinTree BST, ElementType X){
//递归出口
BinTree pre=NULL, p=BST;
BinTree root_NextTree=NULL, p_NextTree=NULL;
if( BST == NULL ){
printf("Not Found\n");
return NULL;
}
//find stage
while( p->Data != X){
if( X < p->Data){
if(p->Left){
pre = p;
p = p->Left;
}else{
break;
}
}else if( X > p->Data ){
if( p->Right ){
pre = p;
p = p->Right;
}else{
break;
}
}
}
if( p->Data != X){
printf("Not Found\n");
return BST;
}
/* 现在节点存在并且被找到 */
//存在右子树, 需要从里面找出最小值
if(p->Right){
//递归出口------>> p的右子树无左子树,将被提上来
if(p->Right == FindMin(p->Right)){
//如果该节点p 为根节点,直接拿掉
if(pre == NULL){
p_NextTree = p->Right;
free(p);
return p_NextTree;
}
else{// 不为根节点
// p 为pre 的右节点
if(pre->Right == p){
pre->Right = p->Right;
}else{
// p 为 pre 的左节点
pre->Left = p->Right;
}
free(p);
return BST;
}
}else{ //需要递归的往下处理
//不用考虑被删除节点是否为根节点
//因为会有节点被上拉,根节点不被删除,仅仅被修改数值
p_NextTree = FindMin(p->Right);
BinTree p_raw = p;
//find stage, 找到p->next(被上拉的节点)的父亲节点,肯定能找到
X = p_NextTree->Data;
/* 搜索模板 */
while( p->Data != X){
if( X < p->Data){
if(p->Left){
pre = p;
p = p->Left;
}else{
break;
}
}else if( X > p->Data ){
if( p->Right ){
pre = p;
p = p->Right;
}else{
break;
}
}
}
//先把改成的值修改,任务交给下一层
p_raw->Data = X;
//接下来的任务交给下层==> 确保下层中的根不会被删除,因为会被删除的情况被上面处理了:
//被提升上来的节点若直接为p的右孩子,就直接作为 递归出口 的情况处理
Delete(pre, X);
return BST;
}
}else{ //被删除节点p 的右子树不存在
p_NextTree = p->Left;
if(pre == NULL){
// 为跟节点被删除
free(p);
return p_NextTree;
}else{ //被删除节点不为根节点
if(pre->Left == p){
pre->Left = p_NextTree;
}else{
pre->Right = p_NextTree;
}
free(p);
return BST;
}
}
}
BinTree Find(BinTree BST, ElementType X){
BinTree p = BST;
if( p == NULL){
return p;
}
while( p->Data != X){
if( X < p->Data){
if(p->Left){
p = p->Left;
}else{
break;
}
}else if( X > p->Data ){
if( p->Right ){
p = p->Right;
}else{
break;
}
}
}
if( p->Data == X){
return p;
}else{
return NULL;
}
}
BinTree FindMin(BinTree BST){
BinTree p = BST;
if(p == NULL){
return p;
}
while( p->Left ){
p = p->Left;
}
return p;
}
BinTree FindMax(BinTree BST){
BinTree p = BST;
if(p == NULL){
return p;
}
while( p->Right ){
p = p->Right;
}
return p;
}
/**
*
*/
BinTree Insert(BinTree BST, ElementType X){
if(BST == NULL){
BST = (BinTree) malloc(sizeof(struct TNode));
BST->Data = X;
BST->Left = NULL;
BST->Right = NULL;
return BST;
}
BinTree p = BST;
while( 1 ){
if( X < p->Data ){
if(p->Left){
p = p->Left;
}else{
BinTree tmp = malloc(sizeof(struct TNode));
tmp->Data = X;
tmp->Left = tmp->Right = NULL;
p->Left = tmp;
return BST;
}
}
if( X > p->Data ){
if( p->Right ){
p = p->Right;
}else{
BinTree tmp = malloc(sizeof(struct TNode));
tmp->Data = X;
tmp->Left = tmp->Right = NULL;
p->Right = tmp;
return BST;
}
}
if( X == p->Data){
printf("Error! %d already existed!\n", X);
exit(1);
}
}
}
----------------------------------------------- line ---------------------------------------------------------
特意学习reference 的做法,真的很妙啊!利用递归之间的来和回,能够简化代码。特在此学习
/**
/**
* 我把它复杂了,没有利用好Delete返回值为所在的根节点这个信息
*/
BinTree Delete(BinTree BST, ElementType X){
if(BST == NULL){
printf("Not Found\n");
return NULL;
}
if(X < BST->Data){
BST->Left = Delete(BST->Left, X);
return BST;
}else if(X > BST->Data){
BST->Right = Delete(BST->Right, X);
return BST;
}
// 找着了
//左右子树都存在
if(BST->Left && BST->Right){
//在右子树里面选择最小的值 或者左子树选最大的
BinTree tmp = FindMin(BST->Right);
BST->Data = tmp->Data;
BST->Right = Delete(BST->Right, tmp->Data);
return BST;
}else{ //最多存在一个孩子
if( !BST->Left && !BST->Right){ //左右子树都不存在
free(BST);
return NULL;
}else if(BST->Left && !BST->Right){ //仅有左子树
BinTree tmp = BST->Left;
free(BST);
return tmp;
}else{ //仅有右子树
BinTree tmp = BST->Right;
free(BST);
return tmp;
}
}
}
原代码如下:
原代码尽量使用迭代和查询已知节点的父节点的方法去完成操作.相当于尾递归,没有利用函数返回值。
BinTree Delete(BinTree BST, ElementType X){
//递归出口
BinTree pre=NULL, p=BST;
BinTree root_NextTree=NULL, p_NextTree=NULL;
if( BST == NULL ){
printf("Not Found\n");
return NULL;
}
//find stage
while( p->Data != X){
if( X < p->Data){
if(p->Left){
pre = p;
p = p->Left;
}else{
break;
}
}else if( X > p->Data ){
if( p->Right ){
pre = p;
p = p->Right;
}else{
break;
}
}
}
if( p->Data != X){
printf("Not Found\n");
return BST;
}
/* 现在节点存在并且被找到 */
//存在右子树, 需要从里面找出最小值
if(p->Right){
//递归出口------>> p的右子树无左子树,将被提上来
if(p->Right == FindMin(p->Right)){
//如果该节点p 为根节点,直接拿掉
if(pre == NULL){
p_NextTree = p->Right;
free(p);
return p_NextTree;
}
else{// 不为根节点
// p 为pre 的右节点
if(pre->Right == p){
pre->Right = p->Right;
}else{
// p 为 pre 的左节点
pre->Left = p->Right;
}
free(p);
return BST;
}
}else{ //需要递归的往下处理
//不用考虑被删除节点是否为根节点
//因为会有节点被上拉,根节点不被删除,仅仅被修改数值
p_NextTree = FindMin(p->Right);
BinTree p_raw = p;
//find stage, 找到p->next(被上拉的节点)的父亲节点,肯定能找到
X = p_NextTree->Data;
/* 搜索模板 */
while( p->Data != X){
if( X < p->Data){
if(p->Left){
pre = p;
p = p->Left;
}else{
break;
}
}else if( X > p->Data ){
if( p->Right ){
pre = p;
p = p->Right;
}else{
break;
}
}
}
//先把改成的值修改,任务交给下一层
p_raw->Data = X;
//接下来的任务交给下层==> 确保下层中的根不会被删除,因为会被删除的情况被上面处理了:
//被提升上来的节点若直接为p的右孩子,就直接作为 递归出口 的情况处理
Delete(pre, X);
return BST;
}
}else{ //被删除节点p 的右子树不存在
p_NextTree = p->Left;
if(pre == NULL){
// 为跟节点被删除
free(p);
return p_NextTree;
}else{ //被删除节点不为根节点
if(pre->Left == p){
pre->Left = p_NextTree;
}else{
pre->Right = p_NextTree;
}
free(p);
return BST;
}
}
}