二叉搜索树的搜索、插入、后继、删除

二叉搜索树

二叉搜索树是以二叉树是形式表示的,这样就可以使用一个二叉链表来表示二叉搜索树了。
对于任何结点x,其左子树的关键字的值不大于x.key,右子树的关键字不小于x.key。

二叉搜索树支持Insert、Delete、Search、Maximum、MInimum、Predecessor(前驱结点)、Successor(后继结点)等操作。二叉搜索树的基本操作与树的高度成正比,对于一个有n个结点的完全二叉树来说,这些操作的最坏运行时间为O(lg n)。若要将n个结点组织成一个线性链,最坏运行时间将达到O(n)。

下面是二叉搜索树的定义:

typedef struct Binary_Search_Tree bTree;
struct struct Binary_Search_Tree
{
    int key;
    bTree *left;
    bTree *right;
    bTree *parent;//指向父结点
}*pNode;

遍历

由于左子树 <= 结点 <= 右子树,所以就可以用中序遍历就可以将一棵二叉搜索树按顺序输出。
Inorder_bTree(pNode *root)
{
    if(root != NULL)
    {
        Inorder_bTree(root->left);
        printf("%d ", root->key);
        Inorder_bTree(root->right); 
    }
}

查找

从根结点开始进行比较,若给定结点的key大于根结点,则沿右子树继续查找;若小于根结点的key,则沿着左子树查找。直到找到返回该结点为止,否则返回NULL。

bTree Search(pNode *root, int key)
{
    if(root == NULL || root->key == key)
        return root;
    if(key < root->key)
        return Search(root->left, key);
    else
        return Search(root->right, key);

}

上面是递归实现的Search,下面是迭代实现的:

bTree Search(pNode *root, int key)
{
    while(root != NULL && root->key != key)
    {
        if(key < root->key)
            root = root->left;
        else
            root = root->right;
    }
    if(root->key == key)
        return root;
    else
        return root;
}

除了上述查找操作,还有Maximum和Minimum,即查找以为某一结点为根的子树中的最小结点(仅以Minimum为例)。

bTree Minimum(pNode *root)
{
    while(root->left != NULL)
        root = root->left;
    return root;
}

前驱和后继

给定一个结点x,大于x.key的最小关键字结点就是x的后继结点。如果后继存在,就返回x的后继结点;若x的关键字就是这棵树中最大的,就返回NULL。

寻找后继结点分两种情况:

  • 结点x的右子树非空。则x的后继几点恰好是x的右子树中的最左的结点,也就是x右子树中的最小结点。(例如根结点15的后继是17)
  • 结点x的右子树为空,并且x有一个后继y。为了找到y,只要从x开始沿树而上,直到遇到一个其双亲有左孩子的结点。(13的后继为15)

这里写图片描述

pTree Successor(pNode *root)
{
//  if(root == Maximum(root))
//      return NULL;
    if(root->right != NULL)
        return Minimum(root->right);

    pNode *y;
    y = root->parent;
    while(y != NULL && root == y->right)
    {
        root = y;
        y = y->parent;
    }
    return y;
}

以上是二叉搜索树的几个简单基本操作。Insert操作仅需要按照二叉搜索树的性质将结点插入相应位置即可。

下面是Insert的代码:

Insert(pNode *root, bTree t)//t为待插入结点
{
    bTree *y, *x;
    y = NULL;
    x = root;
    while(x != NULL)//找到t的双亲结点y
    {
        y = x;
        if(t->key < x->key)
            x = x->left;
        else
            x = x->right;
    }
    t->parent = y;

    if(y == NULL)//待插入二叉树为空
        root = t;
    else if(t->key < y->key)/*判断待插入结点是作为双亲结点的左子树还是右子树*/
        y->left = t;
    else
        y->right = t;
}

最复杂就是删除结点的操作,要用到以上相关操作…………下次再写Delete
————————————————————————————————————
接着昨天未完成的Delete函数

对于删除一棵二叉搜索树的节点x来说,分以下三种情况:

  • 被删除结点为叶子结点。一种可能该结点是根结点,直接删除,并修改指针即可;另一种可能该结点为非根叶子结点,这样直接将其双亲的左(右)孩子置为空。
  • 只有一个孩子结点(左孩子 or 右孩子)。以仅有左孩子为例,将x的左孩子的双亲指向x的双亲结点,将x的双亲的孩子结点指向x的左孩子。
  • x的左右孩子结点都存在。这种情况下,先找到x的右子树中最小的结点y(y无左子树)。若y的双亲结点是x,那么用y.key替换x.key,再删除y结点街可以了;若y的双亲结点不为x,则让y的双亲的孩子指向y的右孩子,y的右孩子的双亲指向y的双亲,最后用y.key替换x.key,再free掉y即可。

下面是Delete函数:

void Delete(pNode *root, bTree x)
{
    if(x == NULL)
        return;
    if(x->left == NULL && x->right == NULL)//x是叶子结点的情况
    {
        if(x->parent == NULL)//x是根结点
        {  
            free(x);  
            (*root)=NULL;  
        }
        else  //x是非根叶子结点
        {
            if(x->parent->left == x)  
                x->parent->left=NULL;  
            else
                x->parent->right=NULL;  
            free(x);  
        }
    }
    else if(x->right != NULL && x->left== NULL)//只有右子树的情况
    {
        x->right->parent = x->parent;
        if(x->parent == NULL)  
            *root=x->right;
        else if(x->parent->left == x)
            x->parent->left = x->right;
        else 
            x->parent->right = x->right;
        free(x);
    }

    else if(x->left != NULL && x->right == NULL)//只有左子树的情况
    {
        x->left->parent = x->parent;
        if(x->parent == NULL)  
            *root=x->left;
        else if(x->parent->left == x)
            x->parent->left = x->left;
        else 
            x->parent->right = x->left;
        free(x);
    }
    else//左右孩子都存在的情况
    {
        bTree temp = Minimum(x->right);//找到x的右子树中key最小的结点
        if(temp->parent == x)//temp为x的右子树
        {
            x->right = NULL;
            x->right->parent = NULL:
            x->key = temp->key;
        }
        else
        {
            temp->parent->left = temp->right;
            temp->right->parent = temp->parent;
            x->key = temp->key
        }
        free(temp);
    }
}

总结:本来Delete函数思路不是特别复杂,但是在修改指针时还要判断被删结点x是其双亲结点的左孩子还是右孩子,所以在只有一个子树的情况中,代码显得有些冗杂。

在《算法导论》中,关于二叉搜索树的删除给出了一个Transplant函数作为Delete的内部函数使用,但还是觉得不如自己思路来的清晰。有兴趣的可以查阅该书(第三版)的168页来研究一下。

猜你喜欢

转载自blog.csdn.net/sinat_31508159/article/details/50680614
今日推荐