BST二叉查找树

博客链接:http://blog.csdn.net/lyh__521/article/details/49811149

介绍

如图1就是一颗二叉排序树,也就是二叉查找树,又称二叉搜索树,简称BST

这里写图片描述

  • 二叉排序树包含以下域:
成员含义
left指向左孩子
right指向右孩子
key关键字
parent指向父结点

  • 特点

设 x 为二叉排序树中的一个结点。如果y是x的左子树中的一个结点,则( y->key) <= (x->key);如果y是x的右子树中的一个结点,则(y->key) >= (x->key)。

即任意一个结点的左子树的所有关键字都比他小,右子树的所有关键字都比他大。


  • 关键字有序性

使用中序遍历算法可将一棵BST树的所有关键字按照从小到大的顺序输出。

//中序遍历
void INORDER_Tree_Walk(BS_Tree* root)
{
    if(root != NULL)
    {
         INORDER_Tree_Walk(root->left);  //遍历左子树
         print(root->key);               //输出关键字
         INORDER_Tree_Walk(root->right); //遍历右子树
    }
}

查询二叉查找树

  从根结点开始查找,对经过的每个结点 x 与 要查询的k 比较,如果 k < (x->key) ,则沿着左子树下降;如果 k > (x->key) ,则沿着右子树下降 。未找到,返回NULL。
  所以查找操作最多不会大于树的高度。时间复杂度为O(h),h为树的高度。

例如:查找 13
这里写图片描述

  • 递归查找
BS_Tree*  Tree_Search(BS_Tree* root,int k)
{
    //碰到空结点 或 查询成功
    if(root == NULL || k == root->key )
        return x;
    //大于要查询的关键字
    else if(k < root->key)
    //走向左子树
        return Tree_Search(root->left,k); 
    else
    //走向右子树
        return Tree_Search(root->right,k);
}
  • 非递归查找
BS_Tree* ITERATIVE_Tree_Search(BS_Tree* root,int k)
{
    BS_Tree*     x = root;
    while(x != NULL && k != x->key)
    {
        if(k < x->key)
            x = x->left;
        else
            x = x->right;
    }
    return x;
}

最大关键字

  如果要查找整棵二叉查找树的最大关键字,只要从根结点开始,一直沿着各结点的right 指针(向右)走即可。

如下图:
这里写图片描述

//最大关键字
BS_Tree* Tree_MAXIMUM(BS_Tree* x)
{
    //从 x 结点出发
    while(x->right != NULL)
        x = x->right; //走向右子树

    return x;
}

最小关键字

  如果要查找整棵二叉查找树的最小关键字,只要从根结点开始,一直沿着各结点的left 指针(向左)走即可。

如下图:
这里写图片描述

//最小关键字
BS_Tree* Tree_MINIMUM(BS_Tree* x)
{
    while(x->left != NULL)
        x = x->left;

    return x;
}

前趋与后继

  • 后继

   有时我们需要找到某一个结点 x 的后继结点,即下一个大于 x 关键字的结点。因为结点右子树的所有关键字都比他大,所以如果 x 有右子树,只需要找出右子树的最小关键字。如果x 没有右子树,因为结点的所有左子树的关键字都偏小,所以我们只需要沿着 x 的父结点不断向上找,直到找到一个结点 y ,使得 x 属于 y 的左子树即可,此时y 是 x 的后继。如果返回NULL,说明 x->key 已经是树的最大关键字了。

例如,下图分别查找13(没有右子树)和15的后继:13–>15 、15–>17
这里写图片描述

//后继
BS_Tree* Tree_SUCCESSOR(BS_Tree* x)
{
    if(x->right != NULL)               //x有右子树
        return Tree_MINIMUM(x->right); //返回右子树的最小关键字
    BS_Tree*  y = x->parent;     
    while(y != NULL && x == y->right)  //x 不属于父结点的左子树
    {
        x = y;                         //x 上移到父结点的位置
        y = y->parent;
    }
    return y;
}
  • 前趋

即前一个比 x 小的关键字为 x 的前趋。可以模仿求后继的方法。


插入

插入比较简单,跟着代码注释走一遍就懂了

//插入
void Tree_Insert(BS_Tree* root,BS_Tree* z)
{
    BS_Tree*    p = NULL;
    BS_Tree*    x = root;     //从根结点开始查找
    while(x != NULL)          //x 没走到尽头
    {
        p = x;                //p 始终指向x的父结点
        if(z->key < x->key)   //当前结点大于插入值
            x = x->left;      //x 向左转前进
        else
            x = x->right;     //x 向右转前进
    }
    z->parent = p;
    if(p == NULL)             //这是一棵空树
        root = z;             //设置新插入的结点为根结点
    else if(z->key < p->key)  
        p->left = z;          //将z 插入到左边
    else
        p->right = z;         //将z 插入到右边
}

   从根结点开始,沿树下降,指针 x 跟踪了这条路径,而 p 始终指向 x 的父结点。如果是空树,直接将插入的结点设置为根结点。否则,根据插入值与 x->key 比较的结果,决定继续向左走还是向右走,直到 x 变为NULL,NULL的位置就是我们要插入的位置。


删除

因为要保持二叉查找树的性质,删除操作略有点麻烦。

根据要删除结点 z 的位置,主要分为以下3种情况:


  • 情况1 : z 没有左孩子和右孩子

这里写图片描述

直接删除 z 结点,将 z 所在的位置置为NULL。


  • 情况2 :z 只有一个孩子(左孩子或者右孩子)
    这里写图片描述

只需要直接删除 z ,让 z 的孩子与 z 的父结点相连。将 z 的孩子移到 z 原来的位置即可。


  • 情况3:z 既有左孩子,又有右孩子

这里写图片描述

当 z 结点有两个孩子时,不会直接删掉 z ,而是找到 z 的后继结点,用后继结点的关键字替换掉 z 的关键字,并且删掉后继结点,如上图。因为后继结点 z 的右子树的最小关键字,用 后继替换 z ,就依然能保证 z 位置的左子树偏小,右子树偏大的性质。删掉后继结点的方法使用情况1 或 情况 2。

BS_Tree*  Tree_Delete(BS_Tree* root,BS_Tree* z)
{
    if(z->left == NULL || z->right == NULL) //没孩子或有一个孩子
        BS_Tree*  y = z;
    else
        y = Tree_SUCCESSOR(z);              //z 有两个孩子时求后继
                                            //y 指向真正要删除的结点

    if(y->left != NULL)                     //如果y有左子树
        BS_Tree*  x = y->left;              //x 指向左树
    else                                    
        x = y->right;                       //x 指向右树
                                            //y没有孩子时,x=NULL

    if(x != NULL)                           //如果y有一个孩子
        x->parent = y->parent;         //设置y的孩子的父亲为y的父结点

    if(y->parent == NULL)              //要删除的是根,必只有一个孩子
        root = x;                           //设置孩子为新的根
    else if(y = (y->parent)->left)          //否则如果y是左树
        (y->parent)->left = x;              //设置y的孩子x为左树
    else
        (y->parent)->right = x;

    if(y != z)                              //如果y是后继
        z->key = y->key;                    //用y的关键字替换z
    return y;                               //返回真正删除的位置
}

因为只有 z 有两个孩子时才需要求后继结点,那么 z 此时必然是有右孩子的,所以z 的后继结点是 z 右树的最小关键字。不存在求后继时需要沿着父结点上升的情况。


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值