04——二分搜索树

本文深入探讨了二分查找法及其变种,包括查找多个目标元素的策略,以及二分搜索树的基本概念、插入、查找、删除操作的实现细节。此外,还详细介绍了如何在二分搜索树中实现floor、ceil、predecessor和successor等功能。
摘要由CSDN通过智能技术生成

1.二分查找法变种(要查找的元素有多个)

在这里插入图片描述

在这里插入图片描述

实现
// 二分查找法, 在有序数组arr中, 查找target
// 如果找到target, 返回第一个target相应的索引index
// 如果没有找到target, 返回比target小的最大值相应的索引, 如果这个最大值有多个, 返回最大索引
// 如果这个target比整个数组的最小元素值还要小, 则不存在这个target的floor值, 返回-1
template<typename T>
int floor(T arr[], int n, T target){

    // 寻找比target小的最大索引
    int l = -1, r = n-1;
    while( l < r ){
        int mid = l + (r-l+1)/2;
        if( arr[mid] >= target )
            r = mid - 1;
        else
            l = mid;
    }

    assert( l == r );

    // 如果该索引+1就是target本身, 该索引+1即为返回值
    if( l + 1 < n && arr[l+1] == target )
        return l + 1;

    // 否则, 该索引即为返回值
    return l;
}


// 二分查找法, 在有序数组arr中, 查找target
// 如果找到target, 返回最后一个target相应的索引index
// 如果没有找到target, 返回比target大的最小值相应的索引, 如果这个最小值有多个, 返回最小的索引
// 如果这个target比整个数组的最大元素值还要大, 则不存在这个target的ceil值, 返回整个数组元素个数n
template<typename T>
int ceil(T arr[], int n, T target){

    assert( n >= 0 );

    // 寻找比target大的最小索引值
    int l = 0, r = n;
    while( l < r ){
        // 使用普通的向下取整即可避免死循环
        int mid = l + (r-l)/2;
        if( arr[mid] <= target )
            l = mid + 1;
        else // arr[mid] > target
            r = mid;
    }

    assert( l == r );

    // 如果该索引-1就是target本身, 该索引+1即为返回值
    if( r - 1 >= 0 && arr[r-1] == target )
        return r-1;

    // 否则, 该索引即为返回值
    return r;
}

2.二分搜索树

2.1 定义(与堆区别)

在这里插入图片描述

2.2 插入

在这里插入图片描述

实现
 // 向二分搜索树中插入一个新的(key, value)数据对
void insert(Key key, Value value){
    root = insert(root, key, value);
}

// 向以node为根的二分搜索树中, 插入节点(key, value)
// 返回插入新节点后的二分搜索树的根
Node* insert(Node *node, Key key, Value value){

   if( node == NULL ){
       count ++;
       return new Node(key, value);
   }

   if( key == node->key )
       node->value = value;
   else if( key < node->key )
       node->left = insert( node->left , key, value);
   else    // key > node->key
       node->right = insert( node->right, key, value);

   return node;
}

2.3 查找(contain、search)

// 查看二分搜索树中是否存在键key
bool contain(Key key){
     return contain(root, key);
 }

// 查看以node为根的二分搜索树中是否包含键值为key的节点
bool contain(Node* node, Key key){

    if( node == NULL )
        return false;

    if( key == node->key )
        return true;
    else if( key < node->key )
        return contain( node->left , key );
    else // key > node->key
        return contain( node->right , key );
}

// 在二分搜索树中搜索键key所对应的值。如果这个值不存在, 则返回NULL
Value* search(Key key){
    return search( root , key );
}

// 在以node为根的二分搜索树中查找key所对应的value
// 若value不存在, 则返回NULL
Value* search(Node* node, Key key){

    if( node == NULL )
        return NULL;

    if( key == node->key )
        return &(node->value);
    else if( key < node->key )
        return search( node->left , key );
    else // key > node->key
        return search( node->right, key );
}

2.4 删除

删除最小值

在这里插入图片描述

在这里插入图片描述

实现
// 删除掉以node为根的二分搜索树中的最小节点
// 返回删除节点后新的二分搜索树的根
Node* removeMin(Node* node){

    if( node->left == NULL ){

        Node* rightNode = node->right;
        delete node;
        count --;
        return rightNode;
    }

    node->left = removeMin(node->left);
    return node;
}
删除最大值

在这里插入图片描述

在这里插入图片描述

实现
// 删除掉以node为根的二分搜索树中的最大节点
// 返回删除节点后新的二分搜索树的根
Node* removeMax(Node* node){

    if( node->right == NULL ){

        Node* leftNode = node->left;
        delete node;
        count --;
        return leftNode;
    }

    node->right = removeMax(node->right);
    return node;
}
删除任意值

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

实现
// 删除掉以node为根的二分搜索树中键值为key的节点
// 返回删除节点后新的二分搜索树的根
Node* remove(Node* node, Key key){

    if( node == NULL )
        return NULL;

    if( key < node->key ){
        node->left = remove( node->left , key );
        return node;
    }
    else if( key > node->key ){
        node->right = remove( node->right, key );
        return node;
    }
    else{   // key == node->key

        if( node->left == NULL ){
            Node *rightNode = node->right;
            delete node;
            count --;
            return rightNode;
        }

        if( node->right == NULL ){
            Node *leftNode = node->left;
            delete node;
            count--;
            return leftNode;
        }

        // node->left != NULL && node->right != NULL
        Node *successor = new Node(minimum(node->right));
        count ++;

        successor->right = removeMin(node->right);
        successor->left = node->left;

        delete node;
        count --;

        return successor;
    }
}

2.5 实现floor、celi

// 寻找key的floor值
// 如果不存在key的floor值(key比BST中的最小值还小), 返回NULL
Key* floor(Key key){

    if( count == 0 || key < minimum() )
        return NULL;

    Node *floorNode = floor(root, key);
    return &(floorNode->key);
}

// 在以node为根的二叉搜索树中, 寻找key的floor值所处的节点, 递归算法
Node* floor(Node* node, Key key){

    if( node == NULL )
        return NULL;

    // 如果node的key值和要寻找的key值相等
    // 则node本身就是key的floor节点
    if( node->key == key )
        return node;

    // 如果node的key值比要寻找的key值大
    // 则要寻找的key的floor节点一定在node的左子树中
    if( node->key > key )
        return floor( node->left , key );

    // 如果node->key < key
    // 则node有可能是key的floor节点, 也有可能不是(存在比node->key大但是小于key的其余节点)
    // 需要尝试向node的右子树寻找一下
    Node* tempNode = floor( node->right , key );
    if( tempNode != NULL )
        return tempNode;

    return node;
}




// 寻找key的ceil值
// 如果不存在key的ceil值(key比BST中的最大值还大), 返回NULL
Key* ceil(Key key){

    if( count == 0 || key > maximum() )
        return NULL;

    Node *ceilNode = ceil(root, key);
    return &(ceilNode->key);
}

// 在以node为根的二叉搜索树中, 寻找key的ceil值所处的节点
Node* ceil(Node* node, Key key){

    if( node == NULL )
        return NULL;

    // 如果node的key值和要寻找的key值相等
    // 则node本身就是key的ceil节点
    if( node->key == key )
        return node;

    // 如果node的key值比要寻找的key值小
    // 则要寻找的key的ceil节点一定在node的右子树中
    if( node->key < key )
        return ceil( node->right , key );

    // 如果node->key > key
    // 则node有可能是key的ceil节点, 也有可能不是(存在比node->key小但是大于key的其余节点)
    // 需要尝试向node的左子树寻找一下
    Node* tempNode = ceil( node->left , key );
    if( tempNode != NULL )
        return tempNode;

    return node;
}

2.6 实现predecessor、successor

// 查找key的前驱
// 如果不存在key的前驱(key不存在, 或者key是整棵二叉树中的最小值), 则返回NULL
Key* predecessor(Key key){

    Node *node = search(root, key);
    // 如果key所在的节点不存在, 则key没有前驱, 返回NULL
    if(node == NULL)
        return NULL;

    // 如果key所在的节点左子树不为空,则其左子树的最大值为key的前驱
    if(node->left != NULL)
        return &(maximum(node->left)->key);

    // 否则, key的前驱在从根节点到key的路径上, 在这个路径上寻找到比key小的最大值, 即为key的前驱
    Node* preNode = predecessorFromAncestor(root, key);
    return preNode == NULL ? NULL : &(preNode->key);
}

// 在以node为根的二叉搜索树中, 寻找key的祖先中,比key小的最大值所在节点
// 算法调用前已保证key存在在以node为根的二叉树中
Node* predecessorFromAncestor(Node* node, Key key){

    if(node->key == key)
        return NULL;

    if(key < node->key)
        // 如果当前节点大于key, 则当前节点不可能是比key小的最大值
        // 向下搜索到的结果直接返回
        return predecessorFromAncestor(node->left, key);
    else{
        assert(key > node->key);
        // 如果当前节点小于key, 则当前节点有可能是比key小的最大值
        // 向右继续搜索, 将结果存储到tempNode中
        Node* tempNode = predecessorFromAncestor(node->right, key);
        if(tempNode)
            return tempNode;
        else
            // 如果tempNode为空, 则当前节点即为结果
            return node;
    }
}

    



// 查找key的后继
// 如果不存在key的后继(key不存在, 或者key是整棵二叉树中的最大值), 则返回NULL
Key* successor(Key key){

    Node *node = search(root, key);
    // 如果key所在的节点不存在, 则key没有前驱, 返回NULL
    if(node == NULL)
        return NULL;

    // 如果key所在的节点右子树不为空,则其右子树的最小值为key的后继
    if(node->right != NULL)
        return &(minimum(node->right)->key);

    // 否则, key的后继在从根节点到key的路径上, 在这个路径上寻找到比key大的最小值, 即为key的后继
    Node* sucNode = successorFromAncestor(root, key);
    return sucNode == NULL ? NULL : &(sucNode->key);
}

// 在以node为根的二叉搜索树中, 寻找key的祖先中,比key大的最小值所在节点
// 算法调用前已保证key存在在以node为根的二叉树中
Node* successorFromAncestor(Node* node, Key key){

    if(node->key == key)
        return NULL;

    if(key > node->key)
        // 如果当前节点小于key, 则当前节点不可能是比key大的最小值
        // 向下搜索到的结果直接返回
        return successorFromAncestor(node->right, key);
    else{
        assert(key < node->key);
        // 如果当前节点大于key, 则当前节点有可能是比key大的最小值
        // 向左继续搜索, 将结果存储到tempNode中
        Node* tempNode = predecessorFromAncestor(node->left, key);
        if(tempNode)
            return tempNode;
        else
            // 如果tempNode为空, 则当前节点即为结果
            return node;
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值