二叉排序树的创建与删除(C语言)

本文详细介绍了如何在C语言中创建和处理二叉排序树,包括节点的插入策略以及删除操作中的注意事项,特别强调了为何需要通过地址而非直接传递指针进行修改。讨论了删除操作时可能出现的错误和解决方案,指出直接删除可能导致数据冗余问题。
摘要由CSDN通过智能技术生成

目录

二叉排序树创建

二叉排序树节点的删除

二叉排序树创建

思路如下:

第一步,首先函数传入的参数为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。

如有错误,欢迎评论区批评指正!!!

  • 11
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
排序(Binary Sort Tree或 Binary Search Tree)是一种用于实现数据快速查找的数据结构。它可以方便地进行数据的插入、删除等操作,并且具有较高的查找效率。下面是一个用C语言实现二排序查找算法的示例: ```c #include <stdio.h> #include <stdlib.h> // 二排序的结点定义 typedef struct BSTNode { int data; // 结点数据 struct BSTNode *left; // 左子指针 struct BSTNode *right; // 右子指针 } BSTNode; // 创建新结点 BSTNode* createNode(int data) { BSTNode* newNode = (BSTNode*)malloc(sizeof(BSTNode)); newNode->data = data; newNode->left = NULL; newNode->right = NULL; return newNode; } // 向二排序插入结点 BSTNode* insertNode(BSTNode* root, int data) { if (root == NULL) { return createNode(data); } else if (data < root->data) { root->left = insertNode(root->left, data); } else if (data > root->data) { root->right = insertNode(root->right, data); } return root; } // 在二排序中查找结点 BSTNode* searchNode(BSTNode* root, int data) { if (root == NULL || root->data == data) { return root; } else if (data < root->data) { return searchNode(root->left, data); } else { return searchNode(root->right, data); } } // 中序遍历二排序 void inorderTraversal(BSTNode* root) { if (root != NULL) { inorderTraversal(root->left); printf("%d ", root->data); inorderTraversal(root->right); } } int main() { BSTNode* root = NULL; int data; // 插入结点 root = insertNode(root, 50); root = insertNode(root, 30); root = insertNode(root, 20); root = insertNode(root, 40); root = insertNode(root, 70); root = insertNode(root, 60); root = insertNode(root, 80); // 中序遍历二排序 printf("中序遍历结果:"); inorderTraversal(root); printf("\n"); // 查找结点 printf("请输入要查找的结点:"); scanf("%d", &data); BSTNode* result = searchNode(root, data); if (result != NULL) { printf("找到了结点 %d\n", result->data); } else { printf("未找到结点 %d\n", data); } return 0; } ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

施霁

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值