【算法与数据结构】---二分搜索树

https://blog.csdn.net/LKJgdut/article/details/105649206
代码如下:

//二分查找法

----非递归实现

// 二分查找法,在有序数组arr中,查找target
// 如果找到target,返回相应的索引index
// 如果没有找到target,返回-1
template<typename T>
int binarySearch(T arr[], int n, T target){

    // 在arr[l...r]之中查找target
    int l = 0, r = n-1;
    while( l <= r ){

        //int mid = (l + r)/2;
        int mid = l + (r-l)/2;//mid要在while循环里更新的
        if( arr[mid] == target )
            return mid;

        if( arr[mid] > target )
            r = mid - 1;
        else
            l = mid + 1;
    }

    return -1;
}

----递归实现

template<typename T>
int __binarySearch2(T arr[], int l, int r, T target){

    if( l > r )
        return -1;

    int mid = (l+r)/2;
    if( arr[mid] == target )
        return mid;
    else if( arr[mid] > target )
        return __binarySearch2(arr, 0, mid-1, target);
    else
        return __binarySearch2(arr, mid+1, r, target);

}

template<typename T>
int binarySearch2(T arr[], int n, T target){

    return __binarySearch2( arr , 0 , n-1, target);
}

----测试用例

int main() {

    int n = 1000000;
    int* a = new int[n];
    for( int i = 0 ; i < n ; i ++ )
        a[i] = i;

    // 测试非递归二分查找法
    clock_t startTime = clock();
    for( int i = 0 ; i < 2*n ; i ++ ){
        int v = binarySearch(a, n, i);
        if( i < n )
            assert( v == i );
        else
            assert( v == -1 );
    }
    clock_t endTime = clock();
    cout << "Binary Search (Without Recursion): " << double(endTime - startTime) / CLOCKS_PER_SEC << " s"<<endl;

    // 测试递归的二分查找法
    startTime = clock();
    for( int i = 0 ; i < 2*n ; i ++ ){
        int v = binarySearch2(a, n, i);
        if( i < n )
            assert( v == i );
        else
            assert( v == -1 );
    }
    endTime = clock();
    cout << "Binary Search (Recursion): " << double(endTime - startTime) / CLOCKS_PER_SEC << " s"<<endl;


    delete[] a;

    return 0;
}

二分搜索树的递归实现代码如下:

(注意对比递归方法的宏观语意以及微观上是如何实现的。)

#include <iostream>

using namespace std;

template <typename Key, typename Value>
class BST{
//首先定义二分搜索树结点类
private:
    struct Node{
        Key key;
        Value value;
        Node *left;
        Node *right;

        Node(Key key, Value value){
            this->key = key;
            this->value = value;
            this->left = this->right = NULL;
        }
    };
//下面定义二分搜索树的属性:根节点、二分搜索树元素个数:count;
    Node *root;
    int count;
//初始化二分搜索树,没有结点(根节点为null)
public:
    BST(){
        root = NULL;
        count = 0;
    }
    ~BST(){
    	destroy( root );
        // TODO: ~BST()
    }

    int size(){
        return count;
    }

    bool isEmpty(){
        return count == 0;
    }
    void insert(Key key, Value value){
        root = insert(root, key, value);
    }

    bool contain(Key key){
        return contain(root, key);
    }

    Value* search(Key key){
        return search( root , key );
    }
    // 前序遍历
    void preOrder(){
        preOrder(root);
    }

    // 中序遍历
    void inOrder(){
        inOrder(root);
    }

    // 后序遍历
    void postOrder(){
        postOrder(root);
    }
        // 层序遍历
    void levelOrder(){

        queue<Node*> q;
        q.push(root);
        while( !q.empty() ){

            Node *node = q.front();
            q.pop();

            cout<<node->key<<endl;

            if( node->left )
                q.push( node->left );
            if( node->right )
                q.push( node->right );
        }
    }
    
    // 寻找最小的键值
    Key minimum(){
        assert( count != 0 );
        Node* minNode = minimum( root );
        return minNode->key;
    }

    // 寻找最大的键值
    Key maximum(){
        assert( count != 0 );
        Node* maxNode = maximum(root);
        return maxNode->key;
    }
    // 从二叉树中删除最小值所在节点
    void removeMin(){
        if( root )
            root = removeMin( root );
    }

    // 从二叉树中删除最大值所在节点
    void removeMax(){
        if( root )
            root = removeMax( root );
    }
};

//-----------------------------------------------------------------------**递归添加**
private:
    // 向以node为根的二叉搜索树中,插入节点(key, value)
    // 返回插入新节点后的二叉搜索树的根
    Node* insert(Node *node, Key key, Value value){

        if( node == NULL ){
            count ++;
            return new Node(key, value);
        }
        /*
        如果e比当前根结点的元素小,我们调用 insert(Node *node, Key key, Value value),返回插入元素value后的以node.left为根的左子树的根:node.left。
        在下一层递归的方法中:
        如果node.left不存在,会直接添加到node.left位置;如果node.left存在,继续调用insert,
        看以node.left 为根结点的子树的左孩子与右孩子是否存在....直到某一个node.left或者node.right不存在,就插入,
        或者是直到value等于某个结点的元素,就结束递归,不将value添加到树中。
         */
        if( key == node->key )
            node->value = value;
        else if( key < node->key )
	/*
            返回插入元素e后的以node.left为根的子树的根:node.left,此时由于node.left可能是变化的(可能之前node.left为null,但是现在赋值后不为null)
            因此,我们必须要将当前指向左孩子的指针node.left指向返回的子二分搜索树的根,这样整个二分搜索树就连接起来。。
             */

            node->left = insert( node->left , key, value);
        else    // key > node->key
            node->right = insert( node->right, key, value);
        //如果当前子树根结点不为空,新元素等于当前子树根结点的元素,那么我们不将该元素添加到当前子树根结点的孩子
        //既然如此,我们不进行递归,而是直接返回当前子树的根即可
        return node;
    }
//-------------------------------------------------------------------**搜索**

    // 查看以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 );
    }
        // 在以node为根的二叉搜索树中查找key所对应的value
    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 );
    }

//-----------------------------------------------------------遍历
    // 对以node为根的二叉搜索树进行前序遍历
    void preOrder(Node* node){

        if( node != NULL ){
            cout<<node->key<<endl;
            preOrder(node->left);
            preOrder(node->right);
        }
    }

    // 对以node为根的二叉搜索树进行中序遍历
    void inOrder(Node* node){

        if( node != NULL ){
            inOrder(node->left);
            cout<<node->key<<endl;
            inOrder(node->right);
        }
    }

    // 对以node为根的二叉搜索树进行后序遍历
    void postOrder(Node* node){

        if( node != NULL ){
            postOrder(node->left);
            postOrder(node->right);
            cout<<node->key<<endl;
        }
    }
    //--------------------------------------------------------------------------------**非递归方式实现前序**

    // 二分搜索树的非递归前序遍历
    void preOrderNR()
    {
        //首先,如果当前二分搜索树的根为null,不需要遍历,直接结束程序
        if(root == NULL)
            return;

        //创建一个栈,用于存储结点对象,记录我们下一次操作将要访问哪一些结点,类似于之前说的系统栈的功能!。
        Stack<Node*> nodeStack;
        nodeStack.push(root);//先将最大树的根结点入栈

        //当栈不为null的时候,遍历操作栈中的结点
        while (!nodeStack.isEmpty())
        {
            Node *cur = nodeStack.top();//将栈顶结点出栈,这个结点就是当前我们要处理的结点
            nodeStack.pop();
            cout<<cur->key<<endl;//处理栈顶结点(这里的处理就是打印结点元素)

            /*
            当前结点打印完后,我们要先处理左孩子结点所代表的树(左子树),再处理右孩子结点所代表的树(左子树),为了下一次循环可以先处理左孩子,
            应该先将右孩子结点入栈,再将左孩子结点入栈。下一次循环出栈的时候,就是先处理左孩子结点。这时,又会判断左孩子结点的左右孩子结点是否存在,
            存在同样按右孩子、左孩子的顺序入栈,下一次循环就将左孩子、右孩子分别出栈处理.....
            直到最开始 root的 左孩子结点的左子树(以左孩子结点为根的子树)处理完,接着处理右孩子结点的右子树,直到所有结点处理完毕....

            注意画图分析这种方法!
            中序遍历和后续遍历也可以使用非递归实现,但是这种情况的代码比较复杂,实际应用也很少!不做实现。
             */
            if(cur->right)
                nodeStack.push(cur->right);
            if(cur.left != NULL)
                nodeStack.push(cur->left);
        }

    }

   void destroy(Node* node){

        if( node != NULL ){
            destroy( node->left );
            destroy( node->right );

            delete node;
            count --;
        }
    }
    //------------------------------------------------------------------------------------------删除
    //----------------------------------首先,我们先找到二分搜索树的最大值与最小值
    // 在以node为根的二叉搜索树中,返回最小键值的节点
    Node* minimum(Node* node){
        if( node->left == NULL )
            return node;

        return minimum(node->left);
    }

    // 在以node为根的二叉搜索树中,返回最大键值的节点
    Node* maximum(Node* node){
        if( node->right == NULL )
            return node;

        return maximum(node->right);
    }
	//---------------------------------------------下面进行删除
	// 删除掉以node为根的二分搜索树中的最小节点
    // 返回删除节点后新的二分搜索树的根
    Node* removeMin(Node* node){

        if( node->left == NULL ){

            Node* rightNode = node->right;
            delete node;//等价于node.right = null
            count --;
            return rightNode;//此时node结点被删除了,或者说它失效了,指向它的指针已经指向新的二分搜索树的根 rightNode
        }
//使得原来指向node.left的指针,指向返回的删除了结点后的二分搜索树的根:rightNode
        node->left = removeMin(node->left);
        return node;//最后将删除了结点的树的根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;
    }
//----------------------------------现在,我们尝试删除二分搜索树的任意值()这部分较难
};
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值