数据结构------搜索二叉树的相关操作(递归和非递归版本)

搜索二叉树的概念

        搜索二叉树满足下面两个要求:

(1)它是一棵二叉树

(2)该二叉树中,任意一棵树的根节点值大于它左子树中的所有结点的值,小于右子树中的所有结点的值

        因此对于搜索二叉树的中序遍历来说,它是按由小到大依次递增的顺序排列的。

搜索二叉树的相关操作

        在本文中将介绍搜索二叉树的以下操作:

(1)初始化搜索二叉树

(2)在搜索二叉树中插入指定元素(递归实现)

(3)在搜索二叉树中插入指定元素(非递归实现)

(4)在搜索二叉树中查找指定元素(递归实现)

(5)在搜索二叉树中查找指定元素(非递归实现)

(6)在搜索二叉树中删除指定元素(递归实现)

(7)在搜索二叉树中删除指定元素(非递归实现)

1. 搜索二叉树的表示

        搜索二叉树本质上也是一棵二叉树,所以与二叉树的表示方法相同,可以根节点的指针来表示一棵搜索二叉树。因此,搜索二叉树的结点结构也与二叉树表示相同,可以利用孩子表示法来实现,至于各节点之间的大小对应关系,应在具体插入和删除时来保证。所以结点的结构表示如下:

//定义二叉搜索树的节点结构:孩子表示法                                                                          
typedef char SearchTreeType;
typedef struct SearchTreeNode
{
    SearchTreeType data;
    struct SearchTreeNode* lchild;
    struct SearchTreeNode* rchild;
}SearchTreeNode;

2. 搜索二叉树的初始化

        搜索二叉树的结点结构定于与普通二叉树相同,因此在初始化时也相同。直接将根节点的指针置为空,来表示一棵空的搜索二叉树。

        因为要对根节点的指针进行置空,即修改指针的值/指向,所以,这里在传参时应传递二级指针。

//初始化二叉搜索树
void SearchTreeInit(SearchTreeNode** proot)
{
    if(proot == NULL)
    {
        //非法输入
        return;
    }
    *proot = NULL;
    return;
}

3. 在搜索二叉树中插入节点(递归实现)

        要使在插入元素之后,依然为搜索二叉树,因此还必须满足搜索二叉树的第二个约束条件。所以对指定元素进行插入是,该结点所在的位置应当是固定的。

        首先指定结点应作为某个节点的左孩子或右孩子。如果作为左孩子,则该结点的左子树必须为空,如果作为右孩子,则该节点的右子树必须为空。

        其次作为右子树还是左子树,应根据指定元素与该结点的值进行大小判断,如果指定元素小于该结点的值,则作为该结点的左子树,如果大于,则作为该结点的右子树。

        注意:这里规定搜索二叉树中的结点元素值是不重复的,如果新元素值已在树中存在,则不进行插入。

        所以,需要不断的寻找满足上述两个要求的结点,思路如下:

(1)如果跟结点为空,则直接将根节点的指针指向新节点

(2)如果根节点不为空,则递归遍历寻找至少有一个子树为空的结点。

        a)如果指定元素的值小于根节点的值,则递归遍历左子树,在左子树中寻找满足条件的值

        b)如果指定元素的值大于根节点的值,则递归遍历右子树,在右子树中寻找满足条件的值

        c)如果指定元素的值等于根节点的值,则直接返回,不进行插入。

        代码如下:

//在二叉搜索树中插入元素
void SearchTreeInsert(SearchTreeNode** proot,SearchTreeType value)
{                                                                                                               
    if(proot == NULL)
    {
        //非法输入
        return;
    }

    SearchTreeNode* new_node = CreateNode(value);
    if(*proot == NULL)
    {
        //插入之前树为空,则将根节点的指针指向新创建的节点
        *proot = new_node;
        return;
    }
    //插入之前树不为空,则从根节点开始遍历查找应该插入的位置
    //如果新节点的值小于根节点的值,则新节点应插入到左子树中
    if(value < (*proot)->data)
    {
        //然后递归遍历左子树进行插入
        SearchTreeInsert(&(*proot)->lchild,value);
    }
    //如果新节点的值大于根节点的值,则新节点应插入到右子树中
    else if(value > (*proot)->data)
    {
        //然后递归遍历右子树进行插入
        SearchTreeInsert(&(*proot)->rchild,value);
    }
    else
    {
        //如果相等,作如下规定:二叉搜索树中不允许有相等的节点
        //此时,直接返回即可
        return;
    }
    return;
}

        当搜索二叉树为单支树时,需遍历整棵树中的结点,所以该函数的时间复杂度为:O(n)。

4. 在搜索二叉树中插入指定元素结点(非递归实现)

        在非递归实现中的思路与上述递归实现思路相同,只是在代码实现上略有不同。

(1)如果根节点为空,则使跟结点的指针指向新节点,结束操作

(2)如果根节点不为空,则将根节点作为当前节点cur,一层一层往下循环遍历寻找满足条件的结点

        a)如果指定元素比根节点值小,则将当前节点cur替换为原cur的左子树,在左子树中继续循环寻找

        b)如果指定元素比根节点大,则价将当前节点cur替换为原cur的右子树,在右子树中继续循环寻找

(3)当遍历到某一个结点时,

        a)如果该结点的左子树为空,且指定元素小于该结点的值,则将新节点作为该结点的左子树

        b)如果该结点的右子树为空,且指定元素大于该结点的值,则将新结点作为该结点的右子树

        c)如果指定元素等于该结点的值,则直接结束返回,不进行插入。

        代码如下:

//在二叉搜索树中插入指定元素(非递归)
void SearchTreeInsertByLoop(SearchTreeNode** proot,SearchTreeType value)
{
    if(proot == NULL)
    {
        //非法输入
        return;
    }
    //创建新节点
    SearchTreeNode* new_node = CreateNode(value);
    //如果根节点为空,直接使根节点的指针指向新节点
    if(*proot == NULL)
    {
        *proot = new_node;
        return;
    }
    //从根节点开始循环遍历寻找新节点要插入的节点,该节点的左右子树必须至少有一个为空,
    //且与新节点的大小关系满足搜索二叉树的定义
    //在插入新节点时,要一层一层的往下遍历直到找到可以插入的节点
    SearchTreeNode* cur = *proot;
    while(1)
    {
        //如果某个节点的左子树为空,且value的值小于该节点的值                                                   
        //将新节点作为该节点的左子树
        if(cur->rchild == NULL && value > cur->data)
        {
            cur->rchild = new_node;
            break;
        }
        //如果某个节点的左子树不为空,且value的值小于该节点的值
        //则在该节点的左子树中继续循环遍历查找
        if(value < cur->data)
        {
            cur = cur->lchild;
        }
        //如果某个节点的右子树不为空,且value的值大于该节点的值
        //则在该节点的右子树中继续循环遍历查找
        else if(value > cur->data)
        {
            cur = cur->rchild;
        }
         //如果遇到与某个节点的值相等,则直接结束,不进行插入
        else
        {
            DestroyNode(new_node);//释放新创建的节点                                                            
            break;
        }
    }
    return;
}

5. 在二叉树中查找指定元素(递归)

        在上述的插入操作中,其实已经包含该步操作。当遍历到一个结点,就将指定元素与该结点的值进行比较。思路如下:

(1)如果该树给空,则查找失败

(2)如果树不为空,则比较跟结点与指定元素的大小

        a)如果指定元素比跟结点值小,此时需要在跟结点的左子树中寻找,因此需要递归遍历左子树

        b)如果指定元素比跟结点大,此时需要在跟结点的右子树中寻找,因此需要递归遍历右子树

        c)如果相等,则直接返回根节点的指针。

        代码如下:

//在二叉搜索树中查找指定元素
SearchTreeNode* SearchTreeFind(SearchTreeNode* root,SearchTreeType to_find)
{
    if(root == NULL)                                                                                            
    {
        //空树
        return NULL;
    }
    //首先判断根节点处的值与所要查找元素的大小关系,如果相等,则直接返回根节点的指针
    if(root->data == to_find)
    {
        return root;
    }
    else if(to_find < root->data)//如果要查找节点的值小于根节点的值
    {
        //则在左子树中递归遍历查找
        return SearchTreeFind(root->lchild,to_find);
    }
    else//如果要查找节点的值大于根节点的值
    {
        //则在右子树中递归遍历查找
        return SearchTreeFind(root->rchild,to_find);
    }
}

6.  在二叉树中查找指定元素(非递归)

        非递归实现时与上述递归实现思路相同,

(1)判断跟节点是否为空,为空,则查找失败

(2)不为空,则将根节点作为当前节点cur,

        a)如果指定元素小于根节点的值,则将cur更新为cur的左子树,循环(2)

        b)如果指定元素大于根节点的值,则将cur更新为cur的右子树,循环(2)

        c)如果指定元素等于根节点的值,则返回cur的值

        d)如果遍历到cur为空时还没找到,说明要查找的元素不存在,直接跳出循环返回即可。

        代码如下:

//在二叉搜索树中查找指定元素(非递归)
SearchTreeNode* SearchTreeFindByLoop(SearchTreeNode* root,SearchTreeType to_find)
{
    if(root == NULL)
    {
        //空树                                                                                                  
        return NULL;
    }
    //从根节点开始遍历查找,待找到某个节点
    SearchTreeNode* cur = root;
    while(1)
    {   
        //如果遍历到某个节点时,该节点为空,则说明要查找的值不存在
        if(cur == NULL)
        {
            break;
        }
        //如果要查找节点的值小于根节点的值,则在左子树中循环遍历查找
        if(to_find < cur->data)
        {
            cur = cur->lchild;
        }
        //rug要查找节点的值大于根节点的值,则在该节点的右子树中继续循环遍历查找
        else if(to_find > cur->data)
        {
            cur = cur->rchild;
        }
        else
        {
            return cur;
        }
    }
    return NULL;
}

7. 在二叉搜索树中删除指定元素(非递归实现)

(1)在树中删除元素之前,首先要判断这棵树是否为空,为空则删除失败。

(2)树不为空,此时需要在树中查找指定元素所在的位置。如果没找到,也删除失败

(3)如果找到了,根据删除元素的不同状态可分为以下几种情况讨论。

a)如果要删除的结点没有左右子树

    i)如果此时要删除的结点是根节点,此时直接将存放根节点指针的内存空间置空。

    ii)如果此时要删除的结点不是根节点。

        若要删除结点是其父节点的左子树,此时需要将父节点的左子树置为空

        若要删除节点是其父节点的右子树,此时需要将父节点的右子树置为空

最后释放根节点的内存空间即可。

b)要删除的结点只有左子树

    i)如果此时要删除的结点是根节点,则将存放根节点指针的内存空间用于存放原根节点的左子树的指针

    ii)如果此时要删除的结点不是根节点。

        若要删除结点是其父节点的左子树,则将要删除

c)要删除的结点只有右子树

d)要删除的结点左右子树都有




 











猜你喜欢

转载自blog.csdn.net/sandmm112/article/details/80296615
今日推荐