二叉搜索树

二叉搜索树支持多种动态集合操作。treeSearch、treeMin、treeMax、successorTree(后继)、predecessorTree(前驱)、insert、delete
一棵二叉查找树是按二叉树结构来组织的。用链表来表示二叉查找树。结点的结构如下。
二叉搜索树的关键字的性质:设x为二叉搜索树的一个结点。如果y是x左子树中的一个结点,则y.val<=x.val;如果y是x右子树中的一个结点,则y.val>=x.val。
作用于二叉搜索树上的基本操作的时间与树的高度成正比,一棵含有n个结点的完全二叉树,这些操作的最坏情况运行时间为O(lgn)。

template <class T>
struct listNode
{
    T val;
    listNode *parent;
    listNode *left;
    listNode *right;
    listNode(T & k) :val(k) {
        parent = NULL;
        left = NULL;
        right = NULL;
    }
};

由于二叉搜索树关键字的性质,这里采用中序遍历的方法来输出二叉树。

void inorderTree() {
    if (firstNode != NULL) {
        inorderTree(firstNode->left);
        cout << firstNode->val;
        inorderTree(firstNode->right);
    }
}

在二叉搜索树中查找关键字等于k的结点

listNode* treeSearch(const T &k) {
    if (firstNode == NULL || firstNode->val == k) {
        return firstNode;
    }
    if (firstNode->val < k)
        firstNode = firstNode->right;
    else
        firstNode = firstNode->left;
    treeSearch(firstNode);
}
//非递归的方式
listNode* iterativeSearch(const T &k) {
    while (firstNode&&firstNode->val!=k) {
        if (firstNode->val < k)
            firstNode = firstNode->right;
        else
            firstNode = firstNode->left;
    }
    return firstNode;
}

找出以某个结点为根结点的二叉搜索树的最大值和最小值

listNode* treeMin(listNode* x) {
    listNode* minNode;
    while (x)
    {   
        minNode = x;
        x = x->left;
    }
    return minNode;
}
listNode* treeMax(listNode* x) {
    listNode* maxNode;
    while (x)
    {
        maxNode = x;
        x = x->right;
    }
    return maxNode;
}

找出某个结点的前驱结点与后继结点

//后继结点
listNode* successorTree(listNode* x){
    //如果x有右子树,则其后继就是右子树中的最左结点,也就是求右子树的最小结点。
    if (x->right != NULL) {
        return treeMin(x->right);
    }
    //如果x没有右子树,且x有后继y,则y是x的最低的一个祖先结点,且y的左孩子也是x的
    //祖先结点。
    //可从x向上查找,直至遇到某个是其父结点的左儿子的结点为止。同理可推出求x前驱的方法
    listNode* y = x->parent;
    while (y&&y->right=x) {
        x = y;
        y = y->parent;
    }
    if (y == NULL) return NULL;
    return y;
}
//前驱结点
listNode* predecessorTree(listNode* x) {
    if (x->left != NULL) {
        return treeMax(x->left);
    }
    listNode* y = x->parent;
    while (y&&y->left = x) {
        x = y;
        y = y->parent;
    }
    if (y == NULL) return NULL;
    return y;
}

插入结点

void insertTree(listNode* z) {
    listNode* pNode = NULL;
    //遇到val值相同的结点,将z放到该结点的右子树上
    while (firstNode) {
        pNode = firstNode;
        if (firstNode->val > z->val) {
            firstNode = firstNode->left;
        }
        else
            firstNode = firstNode->right;
    }
    //此时该二叉搜索树为空
    if (pNode == NULL) firstNode = z;

    else if (firstNode == NULL) {
        if (pNode->val > z->val)
            pNode->left = z;
            else pNode->right = z;
    }   
}

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

  1. 如果z没有孩子结点,那么只需简单地删除z结点,修改其父结点,用NULL来代替z
  2. 如果z只有一个孩子,那么将这个孩子提升到树中z的位置,修改其父结点,用z的孩子来代替z
  3. 如果z有两个孩子,那么找z的后继y(一定在z的右子树上),并让y占据树中z的位置

    这里的处理过程综合前面的三种基本情况:

  4. 如果z没有左孩子,那么用右孩子来代替z,这里右孩子可以是NULL(此情况就是z没有孩子结点),也可以不是。

  5. 如果z仅有一个孩子且为其左孩子,那么用左孩子来替换z
  6. z既有左孩子又有右孩子。此时我们用z的后继y来代替z。y一定在z的右子树中,且y没有左孩子。如果y是z的右孩子,则用y代替z
  7. 如果y不是z的右孩子,则用y的右孩子来代替y,将y移除原来位置,成为z的右孩子的父结点,然后替代z,最后修改z的父结点和z的左孩子
//定义一个子过程,该函数是用另一棵子树替换一个子树并成为其双亲的孩子结点
void transplantTree(listNode *u, listNode *v) {
        if (u->parent == NULL) firstNode = v;
        else if (u->parent->left == u) u->parent->left = v;
        else u->parent->right = v;
        //这里允许v结点为NULL,当其不为NULL时,修改其父结点
        if (v != NULL)
            v->parent = u->parent;
        return;
    }
void deleteTree(listNode* z) {
    //z结点只有左子树不为空,将左子树的根结点代替z
    if (z->left != NULL&&z->right == NULL) {
        transplantTree(z, z->left);
    }
    //z结点只有右子树不为空,将右子树的根结点代替z
    else if (z->left == NULL&&z->right != NULL) {
        transplantTree(z, z->right);
    }
    //z结点有左子树也有右子树,需要找出z的后继
    else {
        listNode* y = treeMin(z->right);
        //如果z的后继正好是z的右子树,则用y代替z
        if (y->parent == z) transplantTree(z, z->right);
        //y不是z的后继,将y的右子树移到y的位置(因为y是z的后继,所以y没有左子树),将y代替z的位置
        else
        {
            transplantTree(y, y->right);
            y->right = z->right;
            y->right->parent = y;
            transplantTree(z, y);
            y->left = z->left;
            y->left->parent = y;
        }
    }
    delete z;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值