18. 二叉搜索树

一、什么是二叉搜索树

  二叉搜索树 是一颗特殊的二叉树。它可以为空,如果不为空,它的 非空左子树所有键值小于其根结点的键值非空右子树所有键值大于其根结点的键值,并且它的 左、右子树都是二叉搜索树

二叉搜索树

  二叉搜索树在二叉树的基础上扩展了一些方法。

ADT BinarySearchTree
{
Data:
    二叉树T∈BinarySearchTree, Item∈ElementType,
Operation:
    void PreOrderTraverse(BinarySearchTree T);              // 先序遍历
    void InOrderTraverse(BinarySearchTree T);               // 中序遍历
    void PostOrderTraverse(BinarySearchTree T);             // 后序遍历
    void LevelOrderTraverse(BinarySearchTree T);            // 层次遍历
    Posotion Find(BinarySearchTree T, ElementType X);       // 查找元素X
    Posotion FindMin(BinarySearchTree T);                   // 查找最小元素
    Posotion FindMax(BinarySearchTree T);                   // 查找最大元素
    void Insert(BinarySearchTree T, ElementType X);         // 插入元素X
    ElementType Delete(BinarySearchTree T, ElementType X);  // 删除元素X
} ADT BinarySearchTree;

二、二叉搜索树的表示

typedef int ElementType;

typedef struct TreeNode
{
    ElementType Data;
    struct TreeNode * Left;
    struct TreeNode * Right;
} TreeNode, * BinarySearchTree, * Position;

三、查找元素

/**
 * @brief 查找元素
 * 
 * @param T 二叉搜索树
 * @param X 要查找的元素
 * @return Position 如果找到返回结点的位置,否则返回NULL
 */
Position Find(BinarySearchTree T, ElementType X)
{
    if (T == NULL)
    {
        return NULL;
    }

    while (T)
    {
        if (X > T->Data)
        {
            T = T->Right;                                                       // 往右子树查找
        }
        else if (X < T->Data)
        {
            T = T->Left;                                                        // 往左子树查找
        }
        else
        {
            return T;                                                           // 找到返回结点的位置
        }
    }

    return NULL;
}
/**
 * @brief 查找最小值
 * 
 * @param T 二叉搜索树
 * @return Position 最小值结点的位置
 */
Position FindMin(BinarySearchTree T)
{
    if (T)
    {
        while (T->Left)
        {
            T = T->Left;
        }
    }

    return T;
}
/**
 * @brief 查找最大值
 * 
 * @param T 二叉搜索树
 * @return Position 最大值结点的位置
 */
Position FindMax(BinarySearchTree T)
{
    if (T)
    {
        while (T->Right)
        {
            T = T->Right;
        }
    }

    return T;
}

四、插入元素

/**
 * @brief 插入元素
 * 
 * @param T 二叉搜索树
 * @param X 要插入的元素
 * @return BinarySearchTree 指向二叉搜索树的指针
 */
BinarySearchTree Insert(BinarySearchTree T, ElementType X)
{
    if (T == NULL)
    {
        T = (BinarySearchTree)malloc(sizeof(TreeNode));
        T->Data = X;
        T->Left = T->Right = NULL;
    }
    else
    {
        if (X < T->Data)
        {
            T->Left = Insert(T->Left, X);                                       // 左子树递归插入
        }
        else if (X > T->Data)
        {
            T->Right = Insert(T->Right, X);                                     // 右子树递归插入
        }
        else
        {
            T->Data = X;                                                        // 找到则更新元素
        }
    }

    return T;
}

  我们也可以使用非递归的方式实现插入元素。

/**
 * @brief 插入元素
 * 
 * @param T 二叉搜索树
 * @param X 要插入的元素
 * @return BinarySearchTree 指向二叉搜索树的指针
 */
BinarySearchTree Insert(BinarySearchTree T, ElementType X)
{
    Position Node = T, Parent = T;

    if (T == NULL)
    {
        T = (BinarySearchTree)malloc(sizeof(TreeNode));
        T->Data = X;
        T->Left = T->Right = NULL;
    }
    else
    {
        while (Node)
        {
            Parent = Node;
            if (X < Node->Data)                                                 // 往左子树查找
            {
                Node = Node->Left;
            }
            else if (X > Node->Data)                                            // 往右子树查找
            {
                Node = Node->Right;
            }
            else                                                                // 找到则更新元素
            {
                Node->Data = X;
                return T;
            }
        }
      
        Node = (BinarySearchTree)malloc(sizeof(TreeNode));
        Node->Data = X;
        Node->Left = Node->Right = NULL;
      
        if (X < Parent->Data)                                                   // 插入左子树
        {
            Parent->Left = Node;
        }
        else                                                                    // 插入右子树
        {
            Parent->Right = Node;
        }
    }

    return T;
}

五、删除元素

  二叉搜索树的删除元素分为三种情况:

  • 要删除的结点是 叶结点:直接删除,并修改其父结点指针置为 NULL。
  • 要删除的结点 只有一个孩子:将其父结点的指针指向要删除结点的孩子结点。
  • 要删除的结点 有左、右两颗子树:用另一结点(右子树的最小元素或者左子树的最大元素)替代被删除结点。

删除元素

/**
 * @brief 删除元素
 * 
 * @param T 二叉搜索树
 * @param X 要删除的元素
 * @return BinarySearchTree 指向二叉搜索树的指针
 */
BinarySearchTree Delete(BinarySearchTree T, ElementType X)
{
    Position P = NULL;

    if (T == NULL)
    {
        return NULL;
    }

    if (X < T->Data)
    {
        T->Left = Delete(T->Left, X);                                           // 左子树递归删除
    }
    else if (X > T->Data)
    {
        T->Right = Delete(T->Right, X);                                         // 右子树递归删除
    }
    else
    {
        if (T->Left && T->Right)                                                // 被删除的结点存在左右子树
        {
            P = FindMax(T->Left);                                               // 找到左子树最大值
            T->Data = P->Data;                                                  // 删除右子树最小值
            T->Left = Delete(T->Left, T->Data);                                 // 删除左子树最大值
        }
        else                                                                    // 被删除结点没有子结点或者只有一个子树
        {
            P = T;
            if (T->Left == NULL)                                                // 有右孩子或者无子结点
            {
                T = T->Right;
            }
            else if (T->Right == NULL)                                          // 有左孩子或者无子结点
            {
                T = T->Left;
            }

            free(P);
        }
    }

    return T;
}

  我们也可以使用非递归的方式实现:

/**
 * @brief 删除元素
 * 
 * @param T 二叉搜索树
 * @param X 要删除的元素
 * @return BinarySearchTree 指向二叉搜索树的指针
 */
BinarySearchTree Delete(BinarySearchTree T, ElementType X)
{
    Position P = T;
    Position Parent = NULL;

    if (T == NULL)
    {
        return NULL;
    }

    while (P != NULL)
    {
        if (X < P->Data)
        {
            Parent = P;
            P = P->Left;
        }
        else if (X > P->Data)
        {
            Parent = P;
            P = P->Right;
        }
        else
        {
            break;
        }
    }

    if (P == NULL)
    {
        printf("要删除的元素不存在\n");
        return T;
    }
  
    if (P->Left == NULL)
    {
        T = Shift(T, Parent, P, P->Right);
    }
    else if (P->Right == NULL)
    {
        T = Shift(T, Parent, P, P->Left);
    }
    else
    {
        Position S = P->Right;                                                  // 要删除结点的后继结点
        Position sParent = P;                                                   // 要删除结点的后继结点的父结点
        while (S->Left != NULL)                                                 // 循环结束后,S指向要删除结点的右子树的最小值
        {
            sParent = S;
            S = S->Left;
        }
  
        ElementType Data = S->Data;
        T = Shift(T, sParent, S, S->Right);                                     // 将要删除结点的右子树的最小值的右孩子托孤
        P->Data = Data;                                                         // 要删除结点的数据改为其右子树的最小值
    }

    return T;
}
/**
 * @brief 托孤方法
 * 
 * @param T 二叉搜索树
 * @param Parent 要删除结点的父结点
 * @param Node 要删除的结点
 * @param Child 要删除结点的子结点
 * @return BinarySearchTree 指向二叉搜索树的指针
 */
BinarySearchTree Shift(BinarySearchTree T, Position Parent, Position Node, Position Child)
{
    if (Parent == NULL)
    {
        T = Child;
    }
    else if (Node == Parent->Left)
    {
        Parent->Left = Child;
    }
    else if (Node == Parent->Right)
    {
        Parent->Right = Child;
    }

    free(Node);

    return T;
}
posted @ 2023-07-19 18:36  星光樱梦  阅读(81)  评论(0编辑  收藏  举报