二叉搜索树

概述:

二叉搜索树中的关键词总是以满足二叉搜索树性质的方式来存储:
设x是二叉搜索树中的一个结点。如果y是x左子树中的一个结点,那么y.key<=x.key。如果y是x右子树中的一个结点,那么y.key>=x.key。

Notes:不同于一般二叉树,二叉搜索树的顺序与结点内容即key值有关,而一般二叉树对相应结点的key值没有要求,故若要对一般二叉树进行遍历,则要排序,因此才有常见的先序,中序,后序三种遍历方式

这样一棵树可以使用一个链表数据结构来表示。
对二叉搜索树的基本操作有:
查找(基于关键词、最小值、最大值、后继、前驱)、插入(考虑空树情况)、删除
(时间复杂度均为O(h), h为树的高度)

代码实现
#include<stdio.h>
#include<stdlib.h>

typedef int ElemType;

typedef struct BiTree{//也即三叉链表 二叉链表少了双亲域
    ElemType key;//结点值
    struct BiTree *left;//左孩子
    struct BiTree *right;//右孩子
    struct BiTree *p;//双亲
}BiTNode,*BiTree;

/*基于关键词的查找*/
//递归表示
BiTree Search_BST(BiTree T, ElemType k)
{
    if (T != NULL&&T->key != k)
    {
        if (k < T->key)
            return Search_BST(T->left,k);
        else
            return Search_BST(T->right,k);
    }
    return T;
}
//迭代表示
BiTree Interative_Search_BST(BiTree T, ElemType k)
{
    while (T != NULL&&T->key != k)
    {
        if (k < T->key)
            T = T->left;
    else
            T = T->right;
    }
    return T;
}



/*最大关键字元素和最小关键字元素的查找*/
BiTree Tree_Minimum(BiTree T)
{
    while (T->left != NULL)//找到最左下的元素
        T = T->left;
    return T;
}

BiTree Tree_Maximum(BiTree T)
{
    while (T->right != NULL)//找到最右下的元素
        T = T->right;
    return T;
}

/*对某结点的后继和前驱的查找*/
BiTree  Tree_Successor(BiTree T)//查找后继
{
    if (T->right != NULL)//T有右孩子,返回右子树的最小值
        return Tree_Minimum(T->right);
    BiTree y;
    y = T->p;//y为T的双亲
    while (y != NULL&&T == y->right)//直到找到一个结点是这个结点的双亲的左孩子 返回这个双亲
    {
        T = y;
        y = y->p;
    }
    return y;

}

BiTree Tree_Predecessor(BiTree T)//查找前驱
{
    if (T->left != NULL)
        return Tree_Maximum(T->left);
    BiTree y;
    y = T->p;
    while (y != NULL&&T == y->left)
    {
        T = y;
        y = y->p;
    }
        return y;

}

/*二叉搜索树的插入*/
//note:要对搜索树结构进行修改
void InsertBST(BiTree &T, ElemType e)//传入二叉树的根结点地址  note:《算法导论》传的是T.root 说明定义了另一个结构体存树的相关信息 如根结点地址 结点数等
{
    BiTree z, x, y=NULL;
    //对新增结点进行初始化,现在要修改的是它的双亲和它双亲的left或right
    z = (BiTree)malloc(sizeof(BiTNode));
    if (z != NULL)//空间申请成功
    {
        z->key = e;
        z->left = z->right=NULL;
    }
    x = T;
    while(x != NULL)
    {
        y = x;
        if (z->key < x->key)//z应该插入到左半部分
            x = x->left;
        else
            x = x->right;
    }//循环跳出后 x=NULL, x的位置即z要插入的位置 y的位置为z的双亲的位置
    //下面处理z和y的关系
    z->p = y;

    if (y = NULL)//说明是空树
        T = z;//新插入的z即为根结点
    else if (z->key < y->key)//z为y左孩子
        y->left = z;
    else
        y->right = z;
}
/*这个函数用一个以v为根的子树来替换一个以u为根的子树,二叉树的删除操作要用到*/
void Transplant(BiTree &T, BiTree u, BiTree v)
{
    if (u->p == NULL)//u没有双亲,说明u为根结点
        T = v;
    else if (u->p->left == u)//若u是其双亲的左子树
        u->p->left = v;
    else
        u->p->right = v;
    if (v != NULL)//要考虑v是否为空
        v->p = u->p;
}

/*对二叉树的删除操作*/
void DeleteBST(BiTree &T, BiTree z)//以T为根结点的二叉搜索树中删除结点z
{
    if (z->left == NULL)//第一种情况:要删除的结点没有左孩子 包含了z只有右孩子和右孩子为NULL即z没有孩子的情况 ,z右孩子代替z
        Transplant(T, z, z->right);
    else if (z->right == NULL)//第二种情况:z有且仅有非NULL左孩子(因为左孩子为NULL则说明没有孩子,这种情况上面已经包含了)z左孩子代替z
        Transplant(T, z, z->left);
    else
    {
        BiTree y = Tree_Minimum(z->right);
        if (y != z->right)//若z的右孩子不是z的后继 说明以右孩子为根的子树最小的那个结点才是z的后继  这个时候这个结点一定没有左孩子!!!
        {
            Transplant(T, y, y->right);//用y自己的右孩子来替代y 
        //  y->p = z->p;//???这一步不用吗???不用  因为后面还要将y Transplant到z 故只用处理好p的子树部分即可
            y->right = z->right;//并置y为z的右孩子的双亲
            y->right->p = y;

        }//处理完以后继结点y为根的子树后(包含本来y就是z的右孩子的情况)
        Transplant(T, z, y);
        y->left = z->left;
        y->left->p = y;
    }
}
notes:

1.查询二叉搜索树,对于大多数计算机,用迭代方式比用递归效率要高得多。
2.《算法导论》中函数传入的是T.root 应该有个类似下面形式的结构体定义,T是Tree类型的变量,我的函数中传入的T默认是根结点地址。

typedef struct{
    BiTree root;//根结点地址
    int nodenum;//结点数量
    //其他信息,如树的类型(二叉搜索树,红黑树,AVL树,B-/B+树)blablabla
}Tree;
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值