首页 > 代码库 > 二叉查找树

二叉查找树

一棵树是N个节点和N-1条边的集合。因为,每条边都将某个节点连接到它的父亲,而除去根节点外每一个节点都有一个父亲。

二叉树:每个节点都不能有多于两个的儿子。深度平均值为O(logN)。
使二叉树成为二叉查找树的性质是,对于树中的每个节点X,它的左子树中所有关键字值小于X的关键字值,而它的右子树中所有关键字值大于X的关键字值。

在程序中,一定要记得处理的根节点为空的情况。除了删除节点的操作外,其它操作都比较容易实现,通常是递归地编写这些操作例程。删除节点的规则如下:
  • 如果节点是树叶,立即删除它。
  • 如果节点有一个儿子,那么将该节点的父节点调整为指向该儿子,然后删除该节点。
  • 如果节点有两个儿子,那么查找该节点右子树中的最小数据,用最小数据替换该节点的数据,然后删除拥有最小数据的那个节点。因为右子树中最小数据的节点必定不会有做孩子,所以又回到了情况1或情况2.

例程:
#include <stdio.h>
 
typedef int ElementType;
typedef struct TreeNode *Position;
typedef struct TreeNode *SearchTree;
 
struct TreeNode
{
    // 自身数据加上指向左右子树的指针
    ElementType Element;
    SearchTree Left;
    SearchTree Right;
};
 
SearchTree MakeEmpty(SearchTree T)
{
    if (T != NULL)
    {
        MakeEmpty(T->Left);
        MakeEmpty(T->Right);
        free(T);
    }
    return T;
}
 
// 返回包含查找元素的指针或NULL
Position Find(SearchTree T, ElementType x)
{
    if (T == NULL)
        return NULL;
    else if (T->Element < x)
        return Find(T->Right, x);
    else if (T->Element > x)
        return Find(T->Left, x);
    else
        return T;
}
 
Position FindMin(SearchTree T)
{
    // 这个判断是必须的
    if (T != NULL)
        while (T->Left)
            T = T->Left;
 
    return T;
}
 
// 递归版本
Position FindMin_recurse(SearchTree T)
{
    if (T == NULL)
        return NULL;
    else if (T->Left == NULL)
        return T;
    else
        return FindMin(T->Left);
}
 
Position FindMax(SearchTree T)
{
    // 这个判断是必须的
    if (T != NULL)
        while (T->Right)
            T = T->Right;
 
    return T;
}
 
// 相同节点已在树中,则不做任何操作
// 返回指向新树根的指针
SearchTree Insert(SearchTree T, ElementType x)
{
    if (T == NULL)
    {
        T = malloc(sizeof(struct TreeNode));
        if (T == NULL)
            return NULL;
        T->Element = x;
        T->Left = T->Right = NULL;
    }
    else if (x < T->Element)
        T->Left = Insert(T->Left, x);
    else if (x > T->Element)
        T->Right = Insert(T->Right, x);
 
    return T;
}
 
SearchTree Delete(SearchTree T, ElementType x)
{
    Position tmp;
 
    if (T == NULL)
        return NULL;
 
    if (x < T->Element)
        T->Left = Delete(T->Left, x);
    else if (x > T->Element)
        T->Right = Delete(T->Right, x);
    else    // 找到要删除的节点
    {
        if (T->Left && T->Right)    // 该节点有两个儿子
        {
            tmp = FindMin(T->Right);
            T->Element = tmp->Element;
            T->Right = Delete(T->Right, tmp->Element);
        }
        else    // 有一个儿子或没有儿子
        {
            tmp = T;
            if (T->Left == NULL)
                T = T->Right;
            else if (T->Right == NULL)
                T = T->Left;
            free(tmp);
        }
    }
    return T;
}
 
SearchTree CreateTree()
{
    // 一定要初始化指针,因为Insert函数根据指针是否为空进行插入
    SearchTree t = NULL;
    return t;
}
 
int main()
{
    SearchTree tree = CreateTree();
    int i;
 
    for (i = 0; i < 10; i++)
        tree = Insert(tree, i);
 
    printf("Max = %d\n", FindMin(tree)->Element);
    printf("Max = %d\n", FindMax(tree)->Element);
 
    return 0;
}


普通的二叉查找树期望的任意操作的平均时间为O(logN),而由于在删除的时候,我们总是用右子树的最小节点代替被删除节点,导致左子树比右子树深度更深。

参考:
《数据结构于算法分析》 P70、P73.