二叉搜索树的基本操作

  二叉搜索树(Binary Search Tree)它或者是一棵空树,或者是具有下列性质的二叉树: 若它的左子树不空,则左子树上所有结点的值均小于它的根结点的值; 若它的右子树不空,则右子树上所有结点的值均大于它的根结点的值; 它的左、右子树也分别为二叉搜索树树。

节点设置:

typedef int DataType;

typedef struct BSTreeNode
{
    struct BSTreeNode* _left;
    struct BSTreeNode* _right;
    DataType _data;

}BSTreeNode;

1. 插入节点

  我们知道二叉搜索树的性质是:根节点左边的节点值均小于根节点;根节点右边的值都大于根节点,并且所有子树也满足这个性质。那么当我们插入一个新节点时也必须保证这个性质成立。

1.非递归插入
  1. 如果是一棵空树,那就直接根节点指针指向这个插入节点。

  2. 不是空树,就要找到这个节点的正确插入位置。

  3. 拿待插入的节点值和根节点比较,比根节点小,就往左子树寻找插入位置;比根节点大,就往右子树寻找插入位置。

  4. 找到确定位置后比较待插入节点和它父亲节点的大小,比之小,就插入到左孩子位置;比之大,就插入到右孩子位置。

BSTreeNode* BuyBSTreeNode(DataType x)  //生成新节点
{
    BSTreeNode* NewNode = (BSTreeNode*)malloc(sizeof(BSTreeNode));
    assert(NewNode != NULL);

    NewNode->_data = x;
    NewNode->_left = NULL;
    NewNode->_right = NULL;

    return NewNode;
}


/* 成功返回0,失败返回-1 */
int BSTreeInsert(BSTreeNode** ppTree, DataType x) //插入
{
    assert(ppTree != NULL);

    if (NULL == *ppTree)
    {
        *ppTree = BuyBSTreeNode(x);
        return 0;
    }

    BSTreeNode* parent = NULL;
    BSTreeNode* cur = *ppTree;

    while (cur != NULL)
    {
        if (cur->_data < x) //插入的数大于根节点,往右子树找位置
        {
            parent = cur;
            cur = cur->_right;
        }
        else if (cur->_data > x)//插入的数小于根节点,往左子树找位置
        {
            parent = cur;
            cur = cur->_left;
        }
        else  //相等就不能插入
        {
            return -1;
        }
    }

    if (x > parent->_data) //根据大小选择插入的左节点还是右节点
    {
        parent->_right = BuyBSTreeNode(x);
    }
    else
    {
        parent->_left = BuyBSTreeNode(x);
    }
    return 0;
}
2. 递归插入

  上面介绍的是非递归实现方法,代码比较冗余,接下来介绍递归实现插入操作。

int BSTreeInsertR(BSTreeNode** ppTree, DataType x) //递归插入
{
    if (NULL == *ppTree)
    {
        // *ppTree代表一个节点的左或右子树的地址
        *ppTree = BuyBSTreeNode(x);
        return 0;
    }

    /*这里传参传的是当前节点的左右子树的地址*/
    if ((*ppTree)->_data > x)
    {
        return BSTreeInsertR(&(*ppTree)->_left, x);
    }
    else if ((*ppTree)->_data < x)
    {
        return BSTreeInsertR(&(*ppTree)->_right, x);
    }
    else
    {
        return -1; //插入失败
    }
}

  我们发现递归实现的代码量比非递归的少了很多,这段代码只有插入节点,但是二叉树的节点之间不是要链接起来吗?这里面没有链接代码啊?

return BSTreeInsertR(&(*ppTree)->_left, x);
return BSTreeInsertR(&(*ppTree)->_right, x);

  注意这两句代码,递归时是把根节点的左节点指针或右节点指针的地址传过去了。在下层递归中,解引用根节点得到的就是上层递归的根节点的左节点指针或右节点指针。
  此时让本层根节点指向插入节点,实际上就是上层根节点的左节点指针或右节点指针指向了待插入节点。这样待插入节点就和树中其他节点链接起来了。


2. 查找节点

BSTreeNode* BSTreeFind(BSTreeNode* pTree, DataType x) //查找
{
    BSTreeNode* cur = pTree;

    while (cur != NULL)
    {
        if (cur->_data > x)
        {
            cur = cur->_left;
        }
        else if (cur->_data < x)
        {
            cur = cur->_right;
        }
        else
        {
            return cur;
        }
    }
    return NULL;
}

3. 删除节点

1. 非递归删除

  删除节点的情况比较复杂,因为我们删了一个节点后也要保证搜索树的性质不被改变。为了更好处理,我们把删除分为三种情况:
1. 被删除节点的左子树为空;
2. 被删除节点的右子树为空;
3. 被删除节点的左右子树均不为空

  情况1和情况2还是挺好处理的,找到待删除节点和他的父亲节点,如果是情况1就让它的父亲节点指向它的右子树;如果是情况2就让它的父亲节点指向它的左子树;然后free了这个节点。
这里写图片描述

  情况3是比较麻烦的,应为要删除的节点有两个孩子,处理地不好就会破坏二叉树的结构,这里给出两种解决方案。
方案一:找到待删除的节点cur的右子树的最左节点sub,把sub节点的值赋给cur节点,然后删除sub节点。

方案二:找到待删除的节点cur的左子树的最右节点sub,把sub节点的值赋给cur节点,然后删除sub节点。

  要注意的是我们也要对sub进行分析,根据sub的情况把删除分为两种:
1. sub就是cur的左孩子或右孩子,直接让cur->_right = sub->_right;
如下图所示(红色节点cur, 蓝色节点sub):
这里写图片描述

  1. sub不是cur的孩子,让sub的父亲节点parent->_left = sub->_right;
    如下图所示(红色节点cur, 蓝色节点sub):
    这里写图片描述
int BSTreeRemove(BSTreeNode** ppTree, DataType x) //删除
{
    assert(ppTree != NULL);

    BSTreeNode* parent = NULL;
    BSTreeNode* cur = *ppTree;

    while (cur != NULL)
    {
        if (cur->_data < x)
        {
            parent = cur;
            cur = cur->_right;
        }
        else if (cur->_data > x)
        {
            parent = cur;
            cur = cur->_left;
        }
        else //找到了
        {
            BSTreeNode* del = NULL; //被删除的节点

            if (cur->_left == NULL)// 1.左孩子为空
            {
                if (parent == NULL) //1.1被删除节点无父节点,表示要删除的是根节点
                {
                    *ppTree = cur->_right;
                    del = cur;
                }
                else //1.2 被删除的节点有父节点,parent
                {
                    if (parent->_left == cur) //1.2.1被删除节点是它父节点的左孩子
                    {
                        parent->_left = cur->_right;
                    }
                    else//1.2.2被删除节点是它父节点的右孩子
                    {
                        parent->_right = cur->_right;
                    }
                    del = cur;
                }
            }
            else if (cur->_right == NULL)// 2.右孩子为空
            {
                if (parent == NULL) //2.1被删除节点无父节点,表示要删除的是根节点
                {
                    *ppTree = cur->_left;
                    del = cur;
                }
                else//2.2被删除节点有父节点,parent
                {
                    if (parent->_left == cur)//2.2.1被删除节点是它父节点的左孩子
                    {
                        parent->_left = cur->_left;
                    }
                    else  //2.2.2被删除节点是它父节点的左孩子
                    {
                        parent->_right = cur->_left;
                    }
                    del = cur;
                }
            }
            else // 3.左右孩子都不为空
            {
                //方法一:找到要删除的节点的右子树的最左节点,把这个节点的值赋给要删除的节点,然后删除这个节点
                parent = cur;
                BSTreeNode* sub = cur->_right;
                while (sub->_left != NULL)  //找到cur右子树的最左节点
                {
                    parent = sub;
                    sub = sub->_left;
                }
                del = sub;
                cur->_data = sub->_data;

                if (parent->_left == sub) //3.1找到的sub不是cur的左孩子,此时sub是parent的左孩子
                {
                    parent->_left = sub->_right;
                }
                else //3.2找到的sub就是cur的右孩子(sub可能有右子树,但左子树必定为空)
                {
                    parent->_right = sub->_right;
                }

                //方法二:找左子树的最右节点
                //BSTreeNode* parent = cur;
                //BSTreeNode* sub = cur->_left;
                //while (sub->_right)
                //{
                //  parent = sub;
                //  sub = sub->_right;
                //}
                //del = sub;
                //cur->_data = sub->_data;

                //if (parent->_left == sub)
                //{
                //  parent->_left = sub->_left;
                //}
                //else
                //{
                //  parent->_right = sub->_left;
                //}
            }
            free(del);
            return 0;
        }
    }
}

2. 递归删除
int BSTreeRemoveR(BSTreeNode** ppTree, DataType x) 
{
    if ((*ppTree) == NULL)
    {
        return -1;
    }
    if ((*ppTree)->_data > x)
    {
        return BSTreeRemoveR(&(*ppTree)->_left, x);
    }
    else if ((*ppTree)->_data < x)
    {
        return BSTreeRemoveR(&(*ppTree)->_right, x);
    }
    else 
    {       
        /*找到要删除的节点*/
        BSTreeNode* del = NULL;

        if ((*ppTree)->_left == NULL)  
        {      
            /*左子树为空*/
            del = *ppTree;

    /* *ppTree 是要删除的节点的地址,也是他父亲节点的左孩子的地址*/
            *ppTree = (*ppTree)->_right;
        }
        else if ((*ppTree)->_right == NULL) 
        {     
            /*右子树为空*/
            del = *ppTree;
            *ppTree = (*ppTree)->_left;
        }
        else 
        {
             /*左右子树都不为空*/
            BSTreeNode* cur = *ppTree;
            BSTreeNode* parent = *ppTree;
            BSTreeNode* sub = cur->_right;
            while (sub->_left != NULL)
            {
                parent = sub;
                sub = sub->_left;
            }
            cur->_data = sub->_data;
            del = sub;

            if (parent->_left == sub)
            {
                parent->_left = sub->_right;
            }
            else
            {
                parent->_right = sub->_right;
            }
        }

        free(del);
        return 0;
    }
}

4. 销毁二叉树

void BSTreeDestoryR(BSTreeNode** ppTree)
{
    if (NULL == *ppTree)
        return;

    BSTreeDestoryR(&((*ppTree)->_left));
    BSTreeDestoryR(&((*ppTree)->_right));
    free(*ppTree);
    *ppTree = NULL;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
二叉排序也称为二叉搜索树,它是一种特殊的二叉,满足以下性质: 1. 左子中的所有节点的值均小于根节点的值; 2. 右子中的所有节点的值均大于根节点的值; 3. 左子和右子也都是二叉排序基本操作包括插入节点、删除节点和查找节点。下面是它们的实现: 1. 插入节点: 插入节点操作用于向二叉排序中插入一个新节点。从根节点开始,比较插入节点的值与当前节点的值,如果小于当前节点的值,则进入左子,否则进入右子。直到找到一个空位置,将新节点插入到该位置。 示例代码如下: ```python class TreeNode: def __init__(self, val): self.val = val self.left = None self.right = None def insert_node(root, val): if root is None: return TreeNode(val) if val < root.val: root.left = insert_node(root.left, val) else: root.right = insert_node(root.right, val) return root ``` 2. 删除节点: 删除节点操作用于从二叉排序中删除指定节点。分为三种情况: - 被删除节点没有子节点:直接删除即可; - 被删除节点只有一个子节点:将子节点替代被删除节点的位置; - 被删除节点有两个子节点:找到被删除节点的后继节点(右子中最小的节点),将后继节点的值复制到被删除节点,然后删除后继节点。 示例代码如下: ```python def find_min(node): while node.left is not None: node = node.left return node def delete_node(root, val): if root is None: return root if val < root.val: root.left = delete_node(root.left, val) elif val > root.val: root.right = delete_node(root.right, val) else: if root.left is None: return root.right elif root.right is None: return root.left else: successor = find_min(root.right) root.val = successor.val root.right = delete_node(root.right, successor.val) return root ``` 3. 查找节点: 查找节点操作用于在二叉排序中查找指定值的节点。从根节点开始,比较目标值与当前节点的值,如果小于当前节点的值,则进入左子,否则进入右子。如果找到匹配的节点,则返回该节点;如果遍历完整个仍未找到匹配的节点,则返回空。 示例代码如下: ```python def search_node(root, val): if root is None or root.val == val: return root if val < root.val: return search_node(root.left, val) else: return search_node(root.right, val) ``` 以上是二叉排序基本操作的实现。你可以根据需要调用这些函数来操作二叉排序
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值