二叉搜索树

BY《INTRODUCIRION TO ALGORITHM》

定义和性质:

二叉搜索树是以一棵二叉树来组织的。每个结点包含key,left,right,p属性,分别是结点的关键字,指向结点的左孩子,右孩子和双亲。

性质:
对于任何结点x,其左子树的关键字最大不超过x.key,其右子树的关键字最小不低于x.key。且不同的二叉搜索树可以代表同一组值的集合。

如图所示是一棵二叉搜索树:

这里写图片描述


遍历:

和二叉树的遍历相同,不再赘述。


查询二叉搜索树:

查找:

根据二叉搜索树的性质,如果要查找某一结点的关键字,那么就从根结点开始查找,并沿着这棵树中的一条简单路径向下进行。

伪代码如下:

TREE-SEARCH(x,k)
    if x == NIL or k == x.key
        return x
    if k < x.key
        return TREE-SERACH(x.left,k)
    else return TREE-SERACH(x.right,k)

这是递归的一种写法,当然有不递归的方式,即迭代,且效率比递归要高得多。

伪代码如下:

ITERATIVE-TREE-SEARCH(x,k)
    while x != NIL and k != x.key
        if k < x.key
            x = x.left
        else x = x.right
    return x

最大关键字元素和最小关键字元素

根据二叉搜索树的性质我们能够很容易知道如何查找二叉搜索树的最大最小关键字。

最小关键字:

TREE-MINMUM(x)
    while x.left != NIL
        x = x.left
    return x

最大关键字:

TREE-MAXMUM(x)
    while x.right!= NIL
        x = x.right
    return x

插入和删除:

插入删除会引起由二叉搜索树表示的动态集合的变化。

插入:

将一个新值v插入到一棵二叉搜索树T中。该过程以z结点作为输入,其中z.key = v, z.left = NULL,z.right = NULL。

伪代码:

TREE-INSERT(T,z)
    y = NIL
    x = T.root
    while x != NIL
        y = x
        if z.key < x.key//找到要插入的位置
            x = x.left
        else x = x.right
    z.p = y
    if y == NIL
        T.root = z
    elseif z.key < y.key
        y.left = z
    else y.right = z

删除:

从一棵二叉搜索树T中删除一个结点z的整个策略分为三种基本情况:

  • 如果z没有孩子结点,那么只是简单地将它删除,并修改它的父结点,用NIL作为孩子来替代z。

  • 如果z只有一个孩子,那么将这个孩子提升到树中z的位置上,并修改z的父结点,用z的孩子来替换z。

  • 如果z有两个孩子,那么找z的后继y(一定在z的右子树中),并让y占据树中z的位置。z的原来右子树部分成为y的新的右子树,并且z的左子树成为y 的新的左子树。(此情况稍显麻烦)。

接下来有几种情况与上面的三种情况有些不同

  • 如果z没有左孩子,那么直接用其右孩子替换z。右孩子可以是NIL,如果是,则此情况成为了没有孩子结点的情形。

  • 如果z仅有一个孩子且为其左孩子,那么直接用其左孩子替换z。

  • 如果z既有一个左孩子又有一个右孩子,查找z的后继y(z的右子树并且没有左孩子),需要将y移出原来的位置进行拼接,并替换树中的z。

  • 如果y是z的右孩子,那么用y替换z,并仅留下y的右孩子。

  • 如果y位于z的右子树中但并不是z的右孩子。先用y的右孩子替换y,再用y替换z。

为了能在二叉搜索树内移动子树,定义一个子过程TRANSPLANT,它是用另一棵子树替换一棵子树并成为双亲的孩子结点。当TRANSPLANT用一棵以v为根的子树来替换一棵以u为根的子树时,结点u的双亲就变为结点v的双亲,并且最后v成为u的双亲的相应孩子。

伪代码如下:

TRANSPLANT(T,u,v)
    if u.p == NIL        //u是树根
        T.root = v
    elseif u == u.p.left
        u.p.left = v
    else u.p.right = v
    if v != NIL         //更新v.p
        v.p = u.p

那么删除结点的过程如下:

TREE-DELETE(T,z)
    if z.left == NIL 
        TRANSPLANT(T,z,z.right)
    elseif z.right == NIL
        TRANSPLANT(T,z,z.left)
    else y = TREE-MINIMUM(z.right)//取右子树中最小元素
        if y.p != z
            TRANSPLANT(T,y,y.right)
            y.right = z.right
            y.right.p = y
        TRANSPLANT(T,z,y)
        y.left = z.left
        y.left.p = y

全部代码实现如下(应该有些小问题)

#include<cstdio>
using namespace std;
struct Node{
    int key;
    Node *left, *right, *par;
    Node():left(NULL), right(NULL), par(NULL){}
};

Node* newnode()
{
    return new Node();
}

Node* root = newnode();

/*找二叉搜索树中的最小值*/ 
int FindMin(Node *u)
{
    Node *x = u;
    while(x->left != NULL)
        x = x->left;
    return x->key;
}
/*找二叉搜索树中的最大值*/ 
int FindMax(Node *u)
{
    Node *x = u;
    while(x->right != NULL)
        x = x->right;
    return x->key;
}
/*插入结点*/
void TreeInsert(int v)
{
    Node* z = newnode();
    z->key = v;
    Node* x = root;
    Node* y = x;
    while(x != NULL)
    {
        y = x;
        if(z->key <= x->key)
            x = x->left;
        else x = x->right;
    }
    z->par = y;
    if(y == NULL)
    {
        root = z;
    }
    else if(z->key <= y->key)
        y->left = z;
    else y->right = z;
}
/*把以v为根的树替换到u*/
void Transplant(Node *u, Node *v)
{
    if(u->par == NULL)
        root = v;
    else if(u == u->par->left)
        u->par->left = v;
    else u->par->right = v;
    if(v != NULL)
        v->par = u->par;
}
/*删除结点*/
void Delete(int v)
{
    Node* z = root;
    while(z != NULL && v != z->key)
    {
        if(v < z->key) z = z->left;
        else z = z->right;
    }
    if(z->left == NULL)
        Transplant(z, z->right);
    else if(z->right == NULL)
        Transplant(z, z->left);
    else
    {
        Node *y = z->right;
        while(y->left != NULL)
        y = y->left;
        if(y->par != z)
        {
            Transplant(y, y->right);
            y->right = z->right;
            y->right->par = y;
        }
        Transplant(z, y);
        y->left = z->left;
        y->left->par = y;
    }
}
/*遍历二叉搜索树*/
void Traverse(Node* x)
{
    if(x != NULL)
    {
        printf("%d ",x->key);
        Traverse(x->left);
        Traverse(x->right);
    }
    else return;
}

int main()
{
    root = NULL;
    while(1)
    {
        printf("1.插入结点  ;   2.删除结点   ;   3.停止  :");
        int c;
        scanf("%d",&c);
        if(c == 1)
        {
            int v;
            scanf("%d",&v);
            TreeInsert(v);
        }else if(c == 2){
            int v;
            scanf("%d",&v);
            Delete(v);
        }else break;
    }
    printf("先序遍历结果:");
    Traverse(root); 
    printf("\n");
    printf("树中最小元素为 %d ; 最大元素为 %d\n", FindMin(root), FindMax(root));
    return 0; 
} 

效果图:

这里写图片描述

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值