目录
二叉排序树创建
思路如下:
第一步,首先函数传入的参数为Tree类型的指针的地址以及需要插入的data;
第二步,判断当前节点是否非空,若是空节点则直接申请内存并构造叶子节点,若非空则比较插入节点的data与当前节点的data;
1. 若大于当前节点,则将当前节点的右节点地址以及data传入函数;
2. 若小于当前节点,则将当前节点的左节点地址以及data传入函数;
3.若等于,则返回ERROR;
注:本次代码构建的二叉搜索树左节点始终小于右节点!!!
bool Insert(Tree *T, int data) {
if(*T == NULL){
*T = (Tree)malloc(sizeof(struct TNode));
(*T)->data = data;
(*T)->left = NULL;
(*T)->right = NULL;
}
else{
if((*T)->data > data){
Insert(&((*T)->left), data);
return true;
}
else if(((*T)->data < data)){
Insert(&((*T)->right), data);
return true;
}
else{
printf("与当前节点值相等!!!");
return ERROR;
}
}
}
易错提醒:函数不能这样写:bool Insert(Tree T, int data),即为什么不能直接传递Tree指针,反而传递当前指针的地址呢?
简单解释如下:这里涉及C语言的规定,在参数传递过程中,将实参复制给形参,这种传递是单向的,只允许实参把值复制给形参,即使形参的值在函数中发生了变化,也不会影响实参。
相信这个简单例子大家都能理解:
// 交换函数的错误写法
void swap1(int a, int b){
int tmp = a;
a = b;
b = tmp;
}
// 正确写法如下:
void swap2(int *a, int *b){
int tmp = *a;
*a = *b;
*b = tmp;
}
int a = 10, b = 20;
swap1(a,b);
printf("交换前a = 10,b = 20,交换后a = %d,b = %d\n", a, b);
//交换前a = 10,b = 20,交换后a = 10,b = 20
swap2(&a, &b);
printf("交换前a = 10,b = 20,交换后a = %d,b = %d\n", a, b);
//交换前a = 10,b = 20,交换后a = 20,b = 10
同样,对于Insert函数也是如此,需要通过传递节点的地址才能对当前节点进行修改!!!
二叉排序树节点的删除
思路如下:
删除操作通过两个函数进行删除,其中Delete是对指定节点的删除,DeleteN是查找需要删除的节点;
下面讲解Delete函数:
第一步,判断当前节点是否为叶子节点,若是,直接删除,若不是,进入第二步;
第二步,判断当前节点的左子树是否非空(包含左右子树均非空的情况,但只要左子树非空,就选取左子树的最大值),若非空,即寻找左子树的最大值替换需要删除的节点值,若空,进入第三步;
第三步,判断当前节点的左右子树是否非空(只包含左子树空,右子树非空的情况),若非空,即寻找右子树的最小值替换需要删除的节点值;
注:整个删除的过程类似节点的上浮!!!
//对指定节点进行删除
bool Delete(Tree *T){
if(!(*T)->left && !(*T)->right){
*T = NULL;
return true;
}
else if((*T)->left){
Tree pre = *T;//删除操作!!!
Tree cur = (*T)->left;
while(cur->right){
pre = cur;
cur = cur->right;
}
(*T)->data = cur->data;
//通过记录的前一个,将其进行删除
if(pre == *T){
Delete(&(pre->left));
}
else{
Delete(&(pre->right));
}
//Delete(&cur);这样有问题
}
else if(!(*T)->left && (*T)->right){
Tree pre = *T;
Tree cur = (*T)->right;
while(cur->left){
pre = cur;
cur = cur->left;
}
(*T)->data = cur->data;
if(pre == *T){
Delete(&(pre->right));
}
else{
Delete(&(pre->left));
}
// Delete(&cur);这样写有问题
}
}
//查找需要删除的节点
void DeleteN(Tree *T, int data){
if(*T == NULL){
printf("需要删除的节点不存在!!!\n");
printf("删除失败!!!\n");
return ;
}
if((*T)->data == data){
Delete(T);
}
else if((*T)->data < data){
DeleteN(&((*T)->right), data);
}
else{
DeleteN(&((*T)->left), data);
}
}
在代码中,对于删除操作有一行我进行了注释,这是我在实际运行过程中遇到的问题,既然每次删除的节点都是最后得到的cur,那为什么不能直接写Delete(&cur),反而要进行判断多此一举呢?
下面是采用注释代码的运行结果:
//中序遍历如下:
//0 1 2 3 4 5 6 7 8 9
//对元素4进行删除
//中序遍历如下:
//0 1 2 2 3 5 6 7 8 9
可以观察到,对4进行删除的确是成功了,但是可以观察到,数字2出现了两次,为什么呢?
刚才我们说到,删除操作类似元素的向上浮动,而数字2所在的节点为叶子节点,虽然向上浮动将4,3进行了覆盖,但对最终的2所在的叶子节点删除失败了!
对于这个问题,可能与节点指针有关系,我的想法是,如果直接删除,相当于与前一个节点直接割裂了,但是如果通过前一个节点直接指向,就避免了这种行为,以多余的2为例,如果通过前一个节点指向这个节点我们设置为NULL,那遍历的时候自然不会打印当前节点的值,但是如果我们只是把当前节点(cur)设置为NULL,这并不是说明当前节点值就不存在了,通过遍历还是能得到原始的值,这就解释了为什么会有两个2。
如有错误,欢迎评论区批评指正!!!