二叉排序树(Binary Sort Tree)学习记录

本文深入探讨了二叉排序树的构建方法,包括迭代法和递归法,并详细解析了查找、插入和删除操作的具体实现。通过实例演示,帮助读者理解二叉排序树的维护和使用。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

最近在学习数据结构基础,学习到二叉排序树的概念,文中讲到二叉排序数查找的时候,讲对集合{62,88,58,47,35,73,51,99,37,93}做查找,查找的前提是,将该集合用排序好的二叉树来存储,如下图文字描述:

根据自己学习的二叉树基础以及上图描述算法,写了下二叉排序树的构建,分别用迭代法和递归。

二叉树结构定义:

typedef struct treeNode{
    int data;
    struct treeNode *left, *right;
}BinTreeNode, *pBinTreeNode;

迭代法:

void BuildSortedBinTree(pBinTreeNode *tRoot, pBinTreeNode *node){
    if (*tRoot == NULL){*tRoot = *node;}
    pBinTreeNode temp = *tRoot;
    while(temp){
        if (temp == *node){return;}
        if ((*node)->data < temp->data){
            if (temp->left == NULL){
                temp->left = *node;
            }
            else{
                temp = temp->left;
            }
        }
        
        if ((*node)->data > temp->data){
            if (temp->right == NULL){
                temp->right = *node;
            }
            else{
                temp = temp->right;
            }
        }
    }
    
}

递归:

void BuildSortedBinTree_2(pBinTreeNode *tRoot, pBinTreeNode *node){

    if (*tRoot == NULL){*tRoot = *node;}
    
    if ((*node)->data < (*tRoot)->data){
        BuildSortedBinTree_2(&(*tRoot)->left, node);
    }
    else if((*node)->data > (*tRoot)->data){
        BuildSortedBinTree_2(&(*tRoot)->right, node);
    }
}

中序遍历打印验证正确性:

void PintTree(pBinTreeNode root){
    if (root == NULL){return;}
    
    PintTree(root->left);
    printf("--data is %d\n", root->data);
    PintTree(root->right);

}

主函数:

#include <stdio.h>
#include <stdlib.h>
int main(){
    
    pBinTreeNode root = NULL;
    int a[] = {62,88,58,47,35,73,51,99,37,93};
    for (int i = 0; i < sizeof(a) / sizeof(a[0]); ++i){
        pBinTreeNode node = (pBinTreeNode)malloc(sizeof(BinTreeNode));
        if (node != NULL){
            memset(node, 0, sizeof(BinTreeNode));
        }
        node->data = a[i];
        BuildSortedBinTree(&root,&node);
    }
    
    PintTree(root);
}

验证结果:

 

OK,我这个人做事情比较着急,其实人家书本后面讲了如何构造一个二叉查找树(尴尬),然后阅读了书中的方法,遂明白作者先将查找的原因,构造二叉查找树,理解为给二叉树插入元素,即在二叉树中查找要插入的元素,如果已经存在,则不插入,不然则按照中序遍历为递增的方式插入,以下为书中代码。

二叉排序树查找:

bool SearchBST(pBinTreeNode T, pBinTreeNode f,  int key, pBinTreeNode *node){
    /*
     pBinTreeNode T:二叉树根
     pBinTreeNode f:用来记录二叉树节点的指针,如果元素存在,f该元素指针,并返回true,若不存在,f为最后一次比较的二叉树节点指针,返回false.
     这样插入函数就知道把新的元素作为谁的孩子了。
     int key:要查找的元素
     pBinTreeNode *node:意义同f,只不过f是递归调用用到,node就只是作为出参。
     */
    if (!T){
        *node = f;
        return false;
    }

    if (key < T->data){
        return SearchBST(T->left, T, key, node);
    }
    else if (key > T->data){
        return SearchBST(T->right, T, key, node);
    }
    else{
        *node = f;
        return true;
    }
}

二叉排序树插入:

void InsertBST(pBinTreeNode *T, int key){
    pBinTreeNode f = NULL;
    pBinTreeNode parents = NULL;
    if (!SearchBST(*T, f, key, &parents)){
        pBinTreeNode newNode = (pBinTreeNode)malloc(sizeof(BinTreeNode));
        if (newNode != NULL){
            newNode->data = key;
            newNode->left = newNode->right = NULL;
        }
        if (*T == NULL){
            *T = newNode;
        }
        else if (key < parents->data){
            parents->left = newNode;
        }
        else{
            parents->right = newNode;
        }
    }
}

二叉排序树删除:

由于删除某个元素之后,希望还保持原始二叉排序树的排序顺序,因此删除的情况比较多,分为以下几种:

(1)要删除的节点是叶子节点,则不影响原始二叉排序树顺序,删除释放即可。

(2)要删除的节点只有左子树:

如下图58节点,只有左子树,要删除他,只需要让58的双亲62的左子树(本来是58)跳过58,指向58的左子树的根,即47即可,即让62->left = 47;free(58);(这里我用数字表示,只是为了通俗易懂,易于理解)。

(3)要删除的节点只有右子树:

如上图中的35,只有右子树,只需要让35的双亲47的左子树(本来是35),跳过35,指向35的右子树37即可。

(4)要删除的节点既有左子树,又有右子树:

书中如下描述,我自己感觉不好表达,直接贴图(我实在是太懒了)

意思就是:比如要删除47,我们找按照中序遍历的47的前驱节点(37),或者后驱节点(48),用前驱节点或者后驱节点来替换47节点的值,这样,我们中序遍历的顺序没有发生变化,删除了某个节点之后,不影响继续对该树进行有序查找或者插入等操作。

个人理解分两步骤:

(1)比如我们用前驱替换,找到47的前驱37,用37替换47的值,然后37需要删除,删除37之后37的前驱就找不到37这个右孩子了,因此还需要替换下37的前驱35的右孩子,替换成谁呢,这里就跟前面(2)(3)情况道理一样的了,37被删除了,那他只有左子树,就将37的前驱也就是37的双亲(35)的右孩子(本来是37),指向37的左孩子36了。

 

说了这么些,也就是帮助自己理解罢了,别的同学估计看着很古怪,希望万一有人翻到了,别误导大家。

二叉树删除操作代码如下:

void DeleteBST(pBinTreeNode *T, int key){
    if(!T){
        return;
    }
    if(key == (*T)->data){
        DeletNode(T);
    }
    else if (key > (*T)->data){
        DeleteBST(&(*T)->right, key);
    }
    else
    {
        DeleteBST(&(*T)->left, key);
    }
}

void DeletNode(pBinTreeNode *p){
    //删除二叉树的一个节点
    pBinTreeNode q;
    if ((*p)->left == NULL){
        q = (*p);
        (*p) = (*p)->right;
        free(q);
    }
    else if ((*p)->right == NULL){
        q = (*p);
        (*p) = (*p)->left;
        free(q);
    }
    else{
        pBinTreeNode s = (*p)->left;//进去要删除的节点的左子树,寻找最右的节点即为要删除节点的前驱
        q = (*p); //q记录s的双亲
        while (s->right){
            q = s; //q记录前驱s的双亲,初始化为(*p)(即当要删除的节点只有一个左孩子和一个右孩子(不存在左右子树)的时候,(*p)即为前驱s的双亲)
            s = s->right;
        }
        (*p)->data = s->data;
        if(q == (*p)){ //要删除的节点只有一个左孩子和一个右孩子(不存在左右子树),则p的前驱是s(p->left),q用来记录s的双亲,此时也就是p,即q == (*p)
            q->left = s->left;
        }
        else{
            q->right = s->left;
        }
        free(s);
    }

}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值