开始了解二叉搜索树。

关于查找,让我们分类别讨论:

如果面对一组无序的数据,可以考虑用顺序查找,时间复杂度为O(N)。

如果面对一组有序的数据,则可以考虑用折半查找,时间复杂度为O(lgN)。

如果面对的有序数据较大,则需要分段折半查找。

如果面对一组无序且较大的数据,此时则要求先排序,再查找。这里就可以利用我们的二叉搜索树。


一.插入数据。

让我们看下面这组数组:array[10]={5,3,4,1,7,8,2,6,0,9}。

我们可以根据以下规则制造一棵二叉搜索树:

1.如果数据进来是空树,则确定该数据为树的根。

2.如果数据进来时树已经有了根,则根据“值大向右,值小向左,值等退出”的规则,知道遇到一片指向空的区域,生成一个新的节点。

根据规则,我们由数组array得到如下二叉搜索树:

关键代码有其二:

1.生成新结点的代码:

    pNode pNew = (pNode)malloc(sizeof(Node));
    //判断开辟空间是否成功?
    if (pNew == NULL)
    {
        printf("malloc failed!
");
        return NULL;
    }
    //初始化
    pNew->data_ = data;
    pNew->left_ = NULL;
    pNew->right_ = NULL;

2.插入数据的代码(这里注意要分开讨论):

!!!注意,代码勿忘对二级指断言判空!!!

情况1:数据插入时是空树。

if (*pRoot == NULL)
{
    *pRoot = BuyBSTreeNode(data);
}

情况2:数据插入时是非空。

//prev用于保存双亲
pNode pCur = *pRoot;
pNode prev = NULL;
//找到插入的合适的位置
while (pCur != NULL)
{
    if (data < pCur->data_)
    {
        prev = pCur;
        pCur = pCur->left_;
    }
    else if (data > pCur->data_)
    {
        prev = pCur;
        pCur = pCur->right_;
    }
    else
    {
        printf("BSTree中已有该值。
");
                return;
    }
}
//进行插入数据步骤
if (data < prev->data_)
{
    pCur = BuyBSTreeNode(data);
    prev->left_ = pCur;
}
else if (data > prev->data_)
{
    pCur = BuyBSTreeNode(data);
    prev->right_ = pCur;
    return 1;
}            

二.查找数据。

如今已经完成了构建二叉搜索树,那么下一步就是查找对应的值。

二叉搜索树查找和折半查找类似,不同的是折半查找时间复杂度稳定,一定是O(lgN),二叉搜索树要看树的构造,越接近完全二叉树,时间复杂度越接近O(lgN),如果得到的二叉搜索树是一棵单支树,那么时间复杂度则有O(N),那么如何调整一课二叉搜索树呢?我们下篇博客见。

那么既然是查找,就也分两种情况:1.找得到。2.找不到。

找到的标志很简单——所求值和内容值相同。找不到的标志——索引指针标至空区域。

查找的规则和插入几乎一致——“值大往右,值小往左,值等则取值,值空则无”。

//pCur为索引指针
while (pCur != NULL)
{
    if (data < pCur->data_)
    {
        pCur = pCur->left_;
    }
    else if (data>pCur->data_)
    {
        pCur = pCur->right_;
    }
    else if (data == pCur->data_)
    {
        return pCur;
    }
}
return NULL;

 三.删除数据

删除数据我们需要分多种情况:

1.需要删除的数据不存在。

2.需要删除的数据为叶子结点。

3.需要删除的数据只有左子树。

4.需要删除的数据只有右子树。

5.需要删除的数据左右子树都存在。

      ↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓

针对这些情况,我们分类讨论,能合并的尽量合并:

1.如果数据不存在,则直接退出函数,因为无法删除指定数据,这也包括树是空树的情况。

2.如果找到需要删除的数据,发现其为叶子节点,则直接让其双亲本来指向它的指针,改为指向NULL。

3.如果找到需要删除的数据,发现其只有左子树,则直接让其双亲本来指向它的指针,改为指向其左子树,这里我们可以看出来,其实2中指向NULL,等同于指向叶子节点的左子树。

4.如果找到需要删除的数据,发现其只有右子树,则直接让其双亲本来指向它的指针,改为指向其右子树。

5.如果找到需要删除的数据,发现其左右子树均存在,则找到上述3或4情况下的结点且满足“一定条件”代替它,然后删除用来替代的结点。

(“一定条件”:这个结点可以是大于该结点的最小结点,也可以是小于该节点的最大结点。)

(此处我们选择用情况4的结点代替情况5的结点,方式是该节点右子树中最左边的结点。)

(例如:如果我们要删 7 ,则用8来代替7的位置,然后本来的8。)

      ↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓

所以我们经过总结,得到如下图四种情况:

1.原图中删10:(未找到)

//////////没找到的情况
//pCur为索引指针
if (pCur == NULL)
{
    printf("The %d is not in BSTree.
", data);
    return 0;
}

2.原图中删2(叶子结点)

3.后续-图中删1(只有左子树)

//prev是标识双亲的指针,pCur为索引指针,下同
if (NULL == pCur->right_)
{
    pDel = pCur;
    //小情况1:所删节点为根,且根无右子树
    if (pCur == (*pRoot))
    {
        *pRoot = pCur->left_;
    }
    //小情况2:所删节点非根
    else
    {
        //要分pDel是prev左或右子树的情况。(这个需要if else)
        if (prev->left_ == pDel)
        {
            prev->left_ = pDel->left_;
        }
        else if (prev->right_ == pDel)
        {
            prev->right_ = pDel->left_;
        }
    }
}

4.原图中删9(只有右子树)

//2.只有右孩子
else if (NULL == pCur->left_)
{
    pDel = pCur;
    //小情况1:所删节点为根,且根无右子树(大情况1里有包含)
    /*if (pCur == *pRoot)
    {
        *pRoot = pCur->left_;
    }*/
    //小情况2:所删节点非根
    //要分pDel是prev左或右子树的情况。(这个需要if else)
    if (prev->left_ == pDel)
    {
        prev->left_ = pDel->right_;
    }
    else if (prev->right_ == pDel)
    {
        prev->right_ = pDel->right_;
    }
}

5.原图中删7(左右孩子都存在)

//找到需要和当前节点交换值得前两张情况的结点。
//需要更新双亲(万一循环进不去)
prev = pCur;
pDel = pCur->right_;
while (pDel->left_)
{
    prev = pDel;
    pDel = pDel->left_;
}
//把值带进去
pCur->data_ = pDel->data_;

//此时pDel可以确定是没有左子树的 
//---要分pDel有无右子树(其实就算右子树是NULL也是直接接)---没有分情况的必要
//和pDel是prev左或右子树的情况。(这个需要if else)
if (prev->left_ == pDel)
{
    prev->left_ = pDel->right_;
}
else if (prev->right_ == pDel)
{
    prev->right_ = pDel->right_;
}

6.在代码中可以看到。其中一种情况,是所删的数据为根,且根只有左子树。如图。

比如我要删二叉搜索树中的5,那很简单,我直接让根结点指向3即可(同样,如果所删为根且根只有右子树,做相应考虑)。

所以!!!很重要的一点,因为可能会要改变根的指向,所以函数中要传二级指针。

综上,为二叉搜索树复习一。


拓展:递归实现插入,查找,删除。

原文地址:https://www.cnblogs.com/shy0322/p/8484858.html