二叉搜索树(Binary Search Tree)

BST的定义

二叉搜索树是这样一种二叉树,对于一个结点x, 如果它的左子树的结点值都小于x,它的右子树的结点值都大于x。并且他的左子树和右子树也满足此特性的二叉树。二叉搜索树的中序遍历可得到数据的有序序列。

BST的前驱和后继

BST结点的前驱是指该结点的前一个结点,即指向该结点的结点。
BST结点的后继是指该结点的右子树中值最接近于该结点值的结点,未必是后一个结点。

BST的插入

二叉搜索树插入结点的过程如下:
1. 如果二叉树是空树,建立根结点。
2. 当前值与根结点的值比较,如果当前值小于根结点值,那么往左子树,如果当前值大于根结点值,则往右子树。
3. 重复步骤2,直到到达叶结点。
4. 根据比较大小结果,建立左或右结点,并赋值。

我们以一个整数序列15, 6, 9, 7, 2, 3, 17, 20, 4, 13, 18来说明整个过程,建立如下图所示的二叉搜索树
二叉搜索树

我们插入结点值为 19 的过程如下所示:
1. 首先 1915 比较,19 > 15 所以走右子树
2. 右子结点值为 17,19 > 17 所以走右子树
3. 右子结点值为 20 19 < 20 所以走左子树
4. 左子结点值为 18 19 > 18 所以走右子树
5. 18 为叶结点,所以将 19 插入到 18 的右子结点

插入过程如下图所示:
插入19的结点

BST结构

BST的结构如下所示:

typedef struct BSTreeNode {
    int elem;
    struct BSTreeNode* leftChild;
    struct BSTreeNode* rightChild;
}BSTreeNode;

其中elem,表示当前结点的值,leftChild, rightChild分别指向该结点的左子结点和右子结点。

BST的中序遍历

中序遍历,就是二叉树的中序遍历,先遍历左子树,再遍历根结点,再遍历右子树,以这样的递归方式进行。遍历代码如下:

void inOrder(struct BSTreeNode* bsTree)
{
    if(bsTree == NULL)
        return;

    inOrder(bsTree->leftChild);
    printf("%d, ", bsTree->elem);
    inOrder(bsTree->rightChild);
    return ;
}

BST的查找

BST的查找类似于二分查找,如果结点的值等于当前结点的值,返回该值,如果结点值小于当前值,继续查找左子树,如果结点的值大于当前的值,继续查找右子树。代码如下所示:

bool find(struct BSTreeNode* bsTree, int elem)
{
    struct BSTreeNode* p = bsTree;
    while(p)
    {
        if(p->elem == elem)
            break;
        else if(elem < p->elem)
            p = p->leftChild;
        else
            p = p->rightChild;
    }

    return p != NULL;
}

BST的删除

BST的查找和遍历都比较容易,但是BST的删除,则有一点点复杂。可以分为以下几种情况
1. 删除叶结点
2. 删除只有左子树的结点
3. 删除只有右子树的结点
4. 删除包含左右子树的结点

删除叶结点

叶结点的删除很容易,直接删除该结点即可,但是需要注意的一点是,我们需要找到该结点的前驱结点,才能将对应的结点指针的内存正确释放。

删除只有左子树的结点

根据二叉树的性质,可以知道,左子结点的值都是小于该结点的值,所以要删除该结点,那么该结点的前驱结点的对应结点,即指向该结点的左子树。

删除只有右子树的结点

根据二叉树的性质,可以知道,右子结点的值都是大于该结点的值,所以要删除该结点,那么该结点的前驱结点的对应结点,即指向该结点的右子树。

删除包含左右子树的结点

要删除该类型的结点,首先要找到该结点的后继,根据二叉树的性质可以知道,该结点的后继位于该结点的右子结点的左子树上,且其后继必没有左子树(因为如果还有左子树,说明还有更小的值更接近该结点的值),那么该结点的右子结点的左子树指针应指向其后继的右子树。

比如说删除结点值 17 的结果如下:
删除结点17

完整代码

完整代码示例如下所示:

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

#define SAFEDELETE(ptr) {if(ptr) {free(ptr); ptr = NULL;}}

typedef struct BSTreeNode {
    int elem;
    struct BSTreeNode* leftChild;
    struct BSTreeNode* rightChild;
}BSTreeNode;

void insertNode(struct BSTreeNode* bsTree, int elem, bool root = false)
{

    if(root == true)
    {
        bsTree->elem = elem;
        bsTree->leftChild = bsTree->rightChild = NULL;
    }else
    {
        struct BSTreeNode* p = bsTree;
        while(p)
        {
            if(elem < p->elem)
            {
                if(p->leftChild != NULL)
                    p = p->leftChild;
                else
                {
                    struct BSTreeNode* q = (struct BSTreeNode*)malloc(sizeof(struct BSTreeNode));
                    q->elem = elem;
                    q->leftChild = q->rightChild = NULL;
                    p->leftChild = q;
                    q = NULL;

                    break;
                }
            }
            else
            {
                if(p->rightChild != NULL)
                    p = p->rightChild;
                else
                {
                    struct BSTreeNode* q = (struct BSTreeNode*)malloc(sizeof(struct BSTreeNode));
                    q->elem = elem;
                    q->leftChild = q->rightChild = NULL;
                    p->rightChild = q;
                    q = NULL;

                    break;
                }

            }
        }
    }

    return ;
}

bool find(struct BSTreeNode* bsTree, int elem)
{
    struct BSTreeNode* p = bsTree;
    while(p)
    {
        if(p->elem == elem)
            break;
        else if(elem < p->elem)
            p = p->leftChild;
        else
            p = p->rightChild;
    }

    return p != NULL;
}

void Delete(struct BSTreeNode* bsTree, int elem)
{
    struct BSTreeNode* p = bsTree, *q = p;
    while(p)
    {
        if(p->elem == elem)
            break;
        else if(elem < p->elem)
            q = p, p = p->leftChild;
        else
            q = p, p = p->rightChild;
    }

    if(p)
    {
        //如果p左、右子结点为空
        if(p->leftChild == NULL && p->rightChild == NULL)
        {
            if(q->leftChild == p)
                SAFEDELETE(q->leftChild)
            else
                SAFEDELETE(q->rightChild)
            p = NULL;
        }
        //如果p左子树不为空,右子树为空
        else if(p->leftChild != NULL && p->rightChild == NULL)
        {
            if(q->leftChild == p)
                q->leftChild = p ->leftChild;
            else
                q->rightChild = p->leftChild;
            p = NULL;
        }
        //如果p右子树不为空,左子树为空
        else if(p->leftChild == NULL && p->rightChild != NULL)
        {
            if(q->leftChild == p)
                q->leftChild = p->rightChild;
            else
                q->rightChild = p ->rightChild;
            p = NULL;
        }
        //如果p左、右子树均不为空
        else
        {
            //查找后继
            struct BSTreeNode* r = p->rightChild;
            while(r->leftChild)
                q = r, r = r->leftChild;

            if(r)
            {
                p->elem = r->elem;
                q->leftChild = r ->rightChild;

                SAFEDELETE(r);
            }

        }
    }
    return ;
}

void inOrder(struct BSTreeNode* bsTree)
{
    if(bsTree == NULL)
        return;

    inOrder(bsTree->leftChild);
    printf("%d, ", bsTree->elem);
    inOrder(bsTree->rightChild);
    return ;
}

void destroyTree(struct BSTreeNode* bsTree)
{
    if(bsTree == NULL)
        return ;
    destroyTree(bsTree->leftChild);
    destroyTree(bsTree->rightChild);

    SAFEDELETE(bsTree);
    return ;
}

int main(int argc, const char * argv[])
{
    int arr[] = {15, 6, 9, 7, 2, 3, 17, 20, 4, 13, 18};
    struct BSTreeNode* bsTree = (struct BSTreeNode*)malloc(sizeof(struct BSTreeNode));

    for(int i = 0 ; i != sizeof(arr) / sizeof(int); ++i)
        insertNode(bsTree, arr[i], i == 0);

    printf("原二叉搜索树中序:");
    inOrder(bsTree);
    printf("\n");

    printf("查找结点值为13的结点:");
    printf("%s\n", find(bsTree, 13) ? "Find it." : "Not Found");

    printf("查找结点值为30的结点");
    printf("%s\n", find(bsTree, 30) ? "Find it." : "Not Found");

    insertNode(bsTree, 16);
    insertNode(bsTree, 19);

    printf("插入16、 19两个结点:");
    inOrder(bsTree);
    printf("\n");

    Delete(bsTree, 15);
    Delete(bsTree, 19);

    printf("删除15、 19两个结点:");
    inOrder(bsTree);
    printf("\n");

    //销毁二叉树
    destroyTree(bsTree);
    return 0;
}

最终的执行结果如下所示:
执行结果

如有错误,望批评指正。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值