C语言手撕实战代码_二叉排序树(二叉搜索树)_构建_删除_插入操作详解

简介: 这份二叉排序树习题集涵盖了二叉搜索树(BST)的基本操作,包括构建、查找、删除等核心功能。通过多个具体示例,如构建BST、查找节点所在层数、删除特定节点及查找小于某个关键字的所有节点等,帮助读者深入理解二叉排序树的工作原理与应用技巧。此外,还介绍了如何将一棵二叉树分解为两棵满足特定条件的BST,以及删除所有关键字小于指定值的节点等高级操作。每个题目均配有详细解释与代码实现,便于学习与实践。
二叉排序树习题
1.设计算法构建一棵二叉排序树(又称二叉搜索树BST)
2.查找二叉排序树中结点为x的结点所在的层数
3.删除二叉排序树T中值为x的结点
4.查找二叉排序树中所有小于key的关键字
5.编写算法,将一棵二叉树t分解成两棵二叉排序树t1和t2,使得t1中的所有结点关键字的值都小于x,t2中所有结点关键字的值都大于x
6.已知二叉排序树中每一个结点值为整型,采用二叉链表存储,编写算法删除二叉排序树中所有关键字小于x的结点

1.设计算法构建一棵二叉排序树(又称二叉搜索树BST)

二叉树搜索树的构建就是多个插入操作的重复使用,下面的代码并用二叉树的前序遍历检验插入结果。
二叉排序树,中序序列序列就变成了有序输出

#include <stdio.h>
#include <stdlib.h>

typedef int ElemType;
typedef struct BiTNode
{
   
   
    ElemType data;
    struct BiTNode *lchild,*rchild;
}BiTNode,*BiTree;


//二叉排序树的插入操作
void insertKey(BiTree &T,ElemType key)
{
   
   
    //if是根(子树根)节点的操作,else if是左右子树操作 根节点+左右子树的操作=问题的解
    if(T==NULL)
    {
   
   
        BiTNode * p=(BiTNode *)malloc(sizeof(BiTNode));
        p->lchild=NULL;
        p->rchild=NULL;
        p->data=key;
        T=p;
    }else if(key<T->data)//如果key<当前结点
    {
   
   
        insertKey(T->lchild, key);
    }else if(key >T->data)
    {
   
   
        insertKey(T->rchild, key);
    }
}

BiTNode * creatBSTree()
{
   
   
    BiTNode *T=NULL;
    ElemType keyList[]={
   
   40,10,45,15};
    int keyListLength=4;
    for(int i=0;i<keyListLength;i++)
    {
   
   
        insertKey(T, keyList[i]);
    }
    return T;
}

void preOreder(BiTree T)
{
   
   
    if(T==NULL) return;
    printf("%d ",T->data);
    preOreder(T->lchild);
    preOreder(T->rchild);
}
int main()
{
   
   
    BiTree T=creatBSTree();
    preOreder(T);
    printf("\n");
}

2.查找二叉排序树中结点为x的结点所在的层数

我的思考?在上一节中,我们可以用层序遍历来完成这个事,但是那个是全部二叉树可用,这一次有什么区别
答:之前的二叉树要遍历完全部的结点,才能找到x。需要横着找
这次有序的寻找就不同了,是竖着找

int nodelevel=0;
int getNode(BiTree T,int x)
{
   
   
    if(T!=NULL)
    {
   
   
        nodelevel++;
        if(x== T->data) return  nodelevel;
        else if(x<T->data)
        {
   
   
            getNode(T->lchild, x);
        }else if(x>T->data)
        {
   
   
            getNode(T->rchild, x);
        }
    }else nodelevel=0;//表明没有查到x,返回0
    return  nodelevel;
}

3.删除二叉排序树T中值为x的结点

找到值为x的结点,然后删除该点
image.png

我们这里以方法1为例子,右孩子,插入到左孩子的最大结点中
思路提炼:
关于存在两棵子树的时候,
找到左子树的最右子树(最大结点),它肯定没有右孩子,将待删除结点的右孩子变成它的右孩子,删掉待删除结点即可

void deleteopr(BiTree &T)
{
   
   
    //第一种情况,删除结点是根节点,第二种情况,只有一个子树,第三种情况,有两个子树
    if(T->lchild==NULL&&T->rchild==NULL)
    {
   
   
        T=NULL;
    }else if(T->lchild!=NULL&&T->rchild!=NULL)
    {
   
   
        BiTNode *pMax=T->lchild;
        while(pMax->rchild!=NULL)
        {
   
   
            pMax=pMax->rchild;
        }
        pMax->rchild=T->rchild;
        BiTNode *p=T;
        T=T->lchild;
        free(p);
    }else         //只有一边有子树
    {
   
   
        //如果左边有子树
        if(T->lchild!=NULL)
        {
   
   
            //记录被抛弃的结点的存储空间,用完它之后就给他free掉
            BiTNode *p=T;
            T=T->lchild;
            free(p);
        }else
        {
   
   
            BiTNode *p=T;
            T=T->rchild;
            free(p);
        }
    }
}
void delete_treeNode(BiTree &T,ElemType x)
{
   
   
    if(T->data==x)
    {
   
   
        //删除这个结点,删除操作开始,否则的话,就继续往左右子树里寻找这个结点
        deleteopr(T);
    }else if(x<T->data)
    {
   
   
        delete_treeNode(T->lchild, x);
    }else if(x>T->data)
    {
   
   
        delete_treeNode(T->rchild, x);
    }
}

4.查找二叉排序树中所有小于key的关键字

思路:二叉排序树中序遍历是有序的,所以在中序遍历的过程中就能找到全部小于key的结点,中止中序遍历就行。

简单代码已省略

5.编写算法,将一棵二叉树t分解成两棵二叉排序树t1和t2,使得t1中的所有结点关键字的值都小于x,t2中所有结点关键字的值都大于x

思路:

6.已知二叉排序树中每一个结点值为整型,采用二叉链表存储,编写算法删除二叉排序树中所有关键字小于x的结点

错误思路:是以为找到某点小于x,就把它连同它的左子树全部删除,若它存在右子树,就让它右子树代替它。这种思路就漏解了,漏掉了小于结点的右子树中,仍可能存在小于x的结点。

image.png

明确使用哪种遍历?-使用前序遍历,因为根节点是划分区间的依据,只有使用前序遍历,我们才能够先拿到根结点。
image.png

void deleteFunc(BiTree &T) //删除一整棵树,先删除左子树,再删除右子树,最后删除根节点,就不用保存根节点的信息了
{
   
   
    if(T!=NULL)
    {
   
   
        deleteFunc(T->lchild);
        deleteFunc(T->rchild);
        free(T);
        T = NULL; // 避免悬空指针,直接将T设为NULL,已经释放掉的指针
    }
}

void preOrderTraversing(ElemType x,BiTree &T)
{
   
   
    //如果树不为空
    if(T!=NULL)
    {
   
   
        if(T->data==x)  //如果当前正正好好,T的左子树小于x,T的右子树大于x,那么删除左子树即可
        {
   
   
            //deleteFunc(T->lchild);  //删除函数,删除了T的左孩子
            T->lchild=NULL;
        }else if(x>T->data) //如果当前x大于该结点,那么该结点的左孩子肯定都比x小,需要删除,右孩子中可能存在着小于x的值,所以右孩子要递归
        {
   
   
            //递归删除右子树
            preOrderTraversing(x,T->rchild);
            //删除左子树和根节点,这里注意删除根节点的时候,它可能存在右孩子,所以要让指针指向他的右孩子,我们保存根节点信息,直接让指针指向其右孩子
            BiTNode* p=T;
            T=T->rchild;

            p->rchild=NULL;   //右子树置空,为什么要把右子树置空,要切断它指针的联系,因为现在的右子树已经是新的根节点了,如果不把老的根节点的右子树置空,删除操作的时候,会把右子树也删除了
           //deleteFunc(p);  //删除根节点和它的左孩子

        }else if(x<T->data) //如果当前x小于该结点,那么该结点的右孩子肯定都比x大,左孩子中可能存在着小于x的值,所以左孩子要递归
        {
   
   
             //右子树不用管,处理它的左子树就行
            preOrderTraversing(x,T->lchild);
        }
    }
}
相关文章
|
1月前
|
存储 自然语言处理 编译器
【C语言】编译与链接:深入理解程序构建过程
【C语言】编译与链接:深入理解程序构建过程
|
1月前
|
存储 搜索推荐 C语言
深入C语言指针,使代码更加灵活(二)
深入C语言指针,使代码更加灵活(二)
|
1月前
|
存储 程序员 编译器
深入C语言指针,使代码更加灵活(一)
深入C语言指针,使代码更加灵活(一)
|
1月前
|
C语言
深入C语言指针,使代码更加灵活(三)
深入C语言指针,使代码更加灵活(三)
深入C语言指针,使代码更加灵活(三)
|
1月前
|
C语言
顺序表数组法构建(C语言描述)
如何使用C语言通过数组方法构建有序顺序表,包括顺序表的创建、插入、删除和打印等。
18 2
|
2月前
|
安全 C语言
在C语言中,正确使用运算符能提升代码的可读性和效率
在C语言中,运算符的使用需要注意优先级、结合性、自增自减的形式、逻辑运算的短路特性、位运算的类型、条件运算的可读性、类型转换以及使用括号来明确运算顺序。掌握这些注意事项可以帮助编写出更安全和高效的代码。
49 4
|
1月前
|
C语言
C语言练习题代码
C语言练习题代码
|
2月前
|
存储 人工智能 C语言
数据结构基础详解(C语言): 栈的括号匹配(实战)与栈的表达式求值&&特殊矩阵的压缩存储
本文首先介绍了栈的应用之一——括号匹配,利用栈的特性实现左右括号的匹配检测。接着详细描述了南京理工大学的一道编程题,要求判断输入字符串中的括号是否正确匹配,并给出了完整的代码示例。此外,还探讨了栈在表达式求值中的应用,包括中缀、后缀和前缀表达式的转换与计算方法。最后,文章介绍了矩阵的压缩存储技术,涵盖对称矩阵、三角矩阵及稀疏矩阵的不同压缩存储策略,提高存储效率。
390 8
|
2月前
|
存储 算法 C语言
C语言手撕实战代码_二叉树_构造二叉树_层序遍历二叉树_二叉树深度的超详细代码实现
这段代码和文本介绍了一系列二叉树相关的问题及其解决方案。其中包括根据前序和中序序列构建二叉树、通过层次遍历序列和中序序列创建二叉树、计算二叉树节点数量、叶子节点数量、度为1的节点数量、二叉树高度、特定节点子树深度、判断两棵树是否相似、将叶子节点链接成双向链表、计算算术表达式的值、判断是否为完全二叉树以及求二叉树的最大宽度等。每道题目均提供了详细的算法思路及相应的C/C++代码实现,帮助读者理解和掌握二叉树的基本操作与应用。
|
1月前
|
C语言 C++
C语言 之 内存函数
C语言 之 内存函数
34 3