二叉搜索树的删,查,插(递归&非递归)

二叉查找树(Binary Search Tree),(又:二叉搜索树,二叉排序树)它是一棵空树,或者是具有下列性质的二叉树: 若它的左子树不空,则左子树上所有结点的值均小于它的根结点的值; 若它的右子树不空,则右子树上所有结点的值均大于它的根结点的值; 它的左、右子树也分别为二叉排序树。如下图示:
这里写图片描述

    int a[] = { 5, 3, 4, 1, 7, 8, 2, 6, 0, 9 };
  1. 非递归操作

    1.1插入节点

BSTreeNode *BuyBSTreeNode(DataType x)//创建新节点
{
    BSTreeNode *node = (BSTreeNode *)malloc(sizeof(BSTreeNode));
    assert(node);
    node->_data = x;
    node->_left = NULL;
    node->_right = NULL;
    return node;
}

int BSTreeInsert(BSTreeNode **ppTree, DataType x)//插入一个节点,因为插入节点是要修改树,所以传的是二级指针,传址方式
{
    BSTreeNode *cur = *ppTree;
    if ((*ppTree) == NULL)
    {
        *ppTree = BuyBSTreeNode(x);//树为空时直接插入
        return 0;
    }
    while (cur)//此时树肯定不为空
    {
        if ((cur)->_data > x)//树的根大于插入的值,则给左插
        {
            if ((cur)->_left == NULL)//它的左边为空,才可以插入
            {
                (cur)->_left = BuyBSTreeNode(x);
                return 0;
            }
            else
            {
                cur = (cur)->_left;//否则继续向左走,直到左边为空
            }
        }
        else if ((cur)->_data < x)//树的根大于插入的值,则给左插
        {
            if ((cur)->_right == NULL)//与左类似
            {
                (cur)->_right = BuyBSTreeNode(x);
                return 0;
            }
            else
            {
                (cur) = (cur)->_right;
            }
        }
        else
        {
            return -1;//此时说明插入的值在树中已经有了,则不能插入,返回-1
        }
    }
    return 0;
}
   1.2查找节点
const BSTreeNode *BSTreeFind(BSTreeNode *tree, DataType x)//查找一个节点
{
    assert(tree);
    while (tree)
    {
        if (tree->_data > x)//从根开始查找,小于根,向左走,大于根,向右走,知道找到为止
        {
            tree = tree->_left;
        }
        else if (tree->_data < x)
        {
            tree = tree->_right;
        }
        else
        {
            return tree;
        }
    }
    return NULL;//表示没有找到,返回空
}
   1.3中序遍历节点
    void BSTreeInOrder(BSTreeNode *tree)//中序遍历
{
    if (tree == NULL)
    {
        return;
    }
    BSTreeInOrder(tree->_left);
    printf("%d ", tree->_data);
    BSTreeInOrder(tree->_right);
}

二叉搜索树用中序遍历出来结果是一组有序排列
测试:

    int a[] = { 5, 3, 4, 1, 7, 8, 2, 6, 0, 9 };
    BSTreeNode *tree = NULL;
    int len = sizeof(a) / sizeof(a[0]);
    for (int i = 0; i < len; i++)
    {
        BSTreeInsert(&tree, a[i]);
    }
    BSTreeInOrder(tree); 
    printf("\n"); 

这里写图片描述

   1.3删除节点

分析:
首先查找元素是否在二叉搜索树中,如果不存在,则返回, 否则要
删除的结点可能分下面四种情况:
a. 要删除的结点无孩子结点
b. 要删除的结点只有左孩子结点
c. 要删除的结点只有右孩子结点
d. 要删除的结点有左、右孩子结点
情况1可以归类到2或者3

int BSTreeRemove(BSTreeNode **ppTree, DataType x)//删除某个节点
{
    assert(*ppTree);
    BSTreeNode *cur = *ppTree;
    BSTreeNode *parent = cur;
    BSTreeNode *sub = NULL;

    while (cur)
    {
        if (cur->_data > x)
        {
            parent = cur;
            cur = cur->_left;
        }
        else if (cur->_data < x)
        {
            parent = cur;
            cur = cur->_right;
        }
        else//找到需要删除的节点
        {
        //分为三种情况操作,1.左为空,2.右为空,3.左右都不为空
            BSTreeNode *del = cur;//删除节点
            if (cur->_left == NULL)//左为空,分为三种情况
            {
                if (parent->_left == cur)//父亲的左节点为当前删除的节点时
                {
                    parent->_left = cur->_right;//当前孩子的右给父亲的左
                }
                else if (parent->_right == cur)//父亲的右节点为当前删除的节点时
                {
                    parent->_right = cur->_right;//当前孩子的右给父亲的右
                }
                else
                {
                    *ppTree = cur->_right;//此时代表树只剩两个节点,删除节点为根,没有父亲,直接使根指向它的右节点
                }

            }
            else if (cur->_right == NULL)//右为空,同左类似
            {
                if (parent->_left == cur)
                {
                    parent->_left = cur->_left;
                }
                else if (parent->_right == cur)
                {
                    parent->_right = cur->_left;
                }
                else
                {
                    *ppTree = cur->_left;
                }

            }
            else if (cur->_left != NULL && cur->_right != NULL)//左右都不为空, 在它的右子树中寻找中序下的第一个结点,用它的值替换被删除节点,来处理该结点的删除问题
            {
                parent = cur;//父亲节点为当前删除节点
                sub = cur->_right;//sub先指向右节点
                while (sub->_left)//查找该节点是否有左节点
                {
                    parent = sub;
                    sub = sub->_left;//有左找到最左值,即是删除节点的右子树中的第一个结点
                }
                cur->_data = sub->_data;//替换删除节点
                del = sub;//将sub给删除节点
                if (parent->_left == sub)//如果sub右子树还有节点,则将他与父亲节点链接
                {
                    parent->_left = sub->_right;
                }
                else
                {
                    parent->_right = sub->_right;
                }
            }
            free(del);//释放删除节点
            del = NULL;
            return 0;
        }
    }
    return -1;
}
  1. 递归
    1.1插入节点
int BSTreeInsertR(BSTreeNode** ppTree, DataType x)
{
    BSTreeNode *cur = *ppTree;
    if ((*ppTree) == NULL)//第一次树为空时,直接插入,以后(*ppTree)接收到的都是上个节点的左子树或者右子树的地址
    {
        *ppTree = BuyBSTreeNode(x);
        return 0;
    }
    if ((cur)->_data > x)
    {
        BSTreeInsertR(&(cur)->_left, x);//此时传的是地址,传到左子树时,左子树为空,则插入该节点,并且直接与上一个节点就链接上了
    }
    else if ((cur)->_data < x)
    {
        BSTreeInsertR(&(cur)->_right, x);
    }
    else
    {
        return -1;
    }
    return 0;
}
    1.2查找节点
const BSTreeNode* BSTreeFindR(BSTreeNode* tree, DataType x)
{
    assert(tree);
    while (tree)
    {
        if (tree->_data > x)
        {
            return BSTreeFindR(tree->_left, x);
        }
        else if (tree->_data < x)
        {
            return BSTreeFindR(tree->_right, x);
        }
        else
        {
            return tree;
        }
    }
    return NULL;

}
    1.3删除节点
int BSTreeRemoveR(BSTreeNode** ppTree, DataType x)
{
    BSTreeNode *del = NULL;
    if (*ppTree == NULL)
    {
        return -1;
    }
    if ((*ppTree)->_data > x)
    {
        BSTreeRemoveR(&((*ppTree)->_left), x);
    }
    else if ((*ppTree)->_data < x)
    {
        BSTreeRemoveR(&((*ppTree)->_right), x);
    }
    else
    {//递归删除都是传址删除,则不需要知道父亲节点,删除后就可以链接
        BSTreeNode *cur = *ppTree;(*ppTee)为上一个节点的左子树或者右子树的地址
        del = cur;
        if (cur->_left == NULL)
        {
            *ppTree = cur->_right;//左为空时,将它的右节点给上一个节点的左子树或者右子树的地址,这样就链接起来
        }
        else if (cur->_right == NULL)
        {
            *ppTree = cur->_left;
        }
        else
        {
            BSTreeNode* sub = cur->_right;
            while (sub->_left)
            {
                sub = sub->_left;
            }
            cur->_data = sub->_data;//此时一样替换后,删除替换节点
            del = sub;
            return BSTreeRemoveR(&((*ppTree)->_right), sub->_data); //递归删除替换节点
        }
        free(del);
        del = NULL;
        return 0;
    }
}

需要源代码请戳这里GitHub

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值