零零散散学算法之详解二叉查找树

本文详细介绍了二叉查找树的原理和操作,包括如何创建遵循特定性质的二叉查找树,如何进行关键字查询、查找最小和最大元素,以及如何插入和删除节点。通过实例和算法解释,帮助读者掌握二叉查找树的核心概念。
摘要由CSDN通过智能技术生成
深入解析二叉查找树

正文

       所谓二叉查找树,实质上是按二叉树的结构来组织的,这样的树可以用链表结构来表示,其中每一个节点都是一个对象。
       二叉查找树中元素(也可称为关键字)的存储方式总是满足以下几个性质:
              1.若二叉树的左子树不为空,则左子树上所有节点的值均不大于其根节点的值;
              2.若二叉树的右子树不为空,则右子树上所有节点的值均不小于其根节点的值;
              3.该二叉树的左子树和右子树也是二叉查找树。


       本文将讨论二叉查找树的创建、查询、插入、删除等操作。

第一节 二叉查找树的创建

       创建一颗二叉查找树相对于创建二叉树来说是比较麻烦的,因为在创建二叉查找树时,必须遵循其性质。但是就是在这样的前提下,我们仍然会遇到麻烦。我们先来看一对二叉查找树,如下图:

       如图中所示,这两颗二叉查找树都有6个节点,且包含同样的关键字,唯一不同的是这两颗查找树的高度不同。a图的高度是2,b图的高度是4。怎么会出现这种情况呢?这就涉及到根节点关键字的选择了。其实根节点关键字的选择比较随意,不同的根节点就可能使创建的二叉查找树的高度不同。

二叉查找树的类型描述如下:
typedef int ElemType;
typedef struct BSTNode{
    ElemType key;	//关键字
    struct BSTNode *left;	//左儿子
    struct BSTNode *right;	//右儿子
    struct BSTNode *p;	//父节点
}BSTNode, *BSTree;
创建二叉查找树的节点如下代码:
BSTree CreateNode(ElemType keynum)
{
    BSTree tree = NULL;

    tree = (BSTree)malloc(sizeof(BSTNode));
    tree->key = keynum;
    tree->left = NULL;
    tree->p = NULL;
    tree->right = NULL;

    return tree;
}
第二节 二叉查找树的查询

二叉查找树的查询操作:查找某一个关键字、查找最小和最大元素、查找前驱和后继。

1.查找某一个关键字

法则:该过程从树的根节点开始查找,并沿着树下降。设要找的那个数为k,对碰到的每个节点x,就比较k和key[x]。如果相同,则查找结束。如果k小于key[x],则继续查找x的左子树(由二叉查找树的性质知,k不可能在x的右子树中)。同理,如果k大于key[x],则在x的右子树中继续查找。例如下图

好,我们来看看查找某个关键字的算法:
BSTree SearchNode(BSTree tree, ElemType k)
{
    while(tree != NULL && k != tree->key)
    {
        if(k < tree->key)  //在左子树中找
            tree = tree->left;
        else
            tree = tree->right;  //在右子树中找
    }
    return tree;
}
2.查找最小和最大元素
       ①.查找最小元素:从根节点开始,沿着各节点的left指针查找下去,直至遇到NULL时为止。如下图:

继续,我们来写一下算法:
BSTree MinNode(BSTree tree)
{
    while(tree->left != NULL)
        tree = tree->left;

    return tree;
}
②.查找最大元素:从根节点开始,沿着各节点的right指针查找下去,直至遇到NULL时为止。如下图:

查找最大元素的算法:
BSTree MaxNode(BSTree tree)
{
    while(tree->right != NULL)
        tree = tree->right;

    return tree;
}
3.查找前驱和后继
       ①.查找后继元素
       定义:如果x有右子树,则x的后继是右子树的最小关键字;否则查看x的祖先节点,直到某个结点y满足此性质:x出现在y的左子树中,如果y存在,则y是x的后继,否则x没有后继。如下图所示:

查找后继元素的算法:
BSTree TreeSuccessor(BSTree tree)
{
    BSTree y;

    if(tree->right != NULL)  //该节点的右子树不为空,找到右子树的最小关键字
        return TreeMinimum(tree->right);
    y = tree->p;
    while(y != NULL && tree == y->right)
    {
        tree = y;
        y = y->p;
    }

    return y;
}
       ②.查找前驱元素
       定义:如果x有左子树,那么前驱是左子树中的最大关键字;否则查看x的祖先节点,直到某个节点y满足此性质:x出现在y的右子树中。如果y存在,则y就是x的前驱,否则x没有前驱。如下图所示:


查找前驱元素的算法:
BSTree TreePrecursor(BSTree tree)
{
    BSTree y;

    if(tree->left != NULL)
        return TreeMaximum(tree->left);
    y = tree->p;
    while(y != NULL && tree == y->left)
    {
        tree = y;
        y = y->p;
    }

    return y;
}
第三节 二叉查找树的插入和删除

1.插入:当讲一个新值x插入到二叉查找树中的适当位置时,设当前插入的位置为z,需满足:z->key = x, z->left = NULL, z->right = NULL。如下图所示:


插入算法如下:
int InsertNode(BSTree *tree, BSTree z)
{
    BSTree x, y;

    y = NULL;
    x = *tree;
    while(x != NULL)
    {
        y = x;
        if(z->key < x->key)
            x = x->left;
        else
            x = x->right;
    }
    z->p = y;
    if(y == NULL)
        *tree = z;
    else if(z->key < y->key)
        y->left = z;
    else
        y->right = z;

    return 0;
}
2.删除:从二叉查找树中删除某一个节点元素可根据其是否有子女节点来操作:
         ①.没有子女节点:将父节点对应子女节点的指针置为NULL。如下图:

         ②.有一个子女节点:将其父节点的指针指向其子女节点。如下图:

         ③.有两个子女节点:
              法一:将当前结点与其左子树中最大的元素交换,然后删除当前结点。
              法二:将当前结点与其右子树中最小的元素交换,然后删除当前结点。


最后我们来看看删除操作的算法:
int TreeDelete(BSTree *tree, BSTree z)
{
    BSTree x, y;

    if(z->left == NULL || z->right == NULL)	//有一个或者没有子女节点
        y = z;
    else
        y = TreeSuccessor(&(**tree));	//有两个子女节点

    if(y->left != NULL)
        x = y->left;
    else
        x = y->right;

    if(x != NULL)
        x ->p = y->p;

    if(y->p == NULL)
        (*tree) = x;
    else if(y == y->p->left)
        y->p->left = x;
    else
        y->p->right = x;

    if(y != z)
        z->key = y->key;
    free(y);
    y = NULL;
    return 0;
}
二叉查找树的操作就说完了。

第四节 结束语
       想想、写写、画画......

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值