二分搜索树

二分搜索树

将数据使用树结构存储后,出奇的高效。
和链表一样,动态数据结构。

二分搜索树是二叉树;
二分搜索数的每个节点值:

  • 大于其左子树的所有节点的值;
  • 小于其右子树的所有节点的值。

二分搜索树存储的元素必须具有可比性

1 添加元素(不包含重复元素)

Alt添加元素的思路:
1)判断,若树为空,则创建一个新的节点,直接使用添加的元素初始化;
2)不为空,则比较添加元素与节点元素的大小,然后使用递归左(右)子树;
3)递归终止条件:节点为空,创建新的节点并初始化返回。

递归实现:

	//向以node为根的二分搜索数中插入元素e,递归算法
    //返回插入新节点后二分搜索树的根
    TNode<T> *add(TNode<T> *node,T e) {
        if(node== nullptr) {
            size++;
            return new TNode<T>(e);
        }

        if(node->e>e){
            node->left=add(node->left,e);//递归
        }else if(node->e<e){
            node->right=add(node->right,e);
        }
        return node;
    }

非递归实现:

    void addNR(T e){
        if(this->root== nullptr) {
            size++;
            this->root=new TNode<T>(e);
            return;
        }
        TNode<T> *node= this->root;
        TNode<T> *newNode=nullptr;
        while (node!= nullptr){
            if(node->e == e)
                return;
            //由于node最后出循环时2存放的是nullptr,所有需要保存它的父节点
            newNode=node;
            node->e > e ?node=node->left :node=node->right;
        }
        newNode->e > e ?  newNode->left=new TNode<T>(e) : newNode->right=new TNode<T>(e);
        size++;
    }

Alt

2 前序遍历

Alt
Alt前序遍历、中序遍历、后序遍历的概念就不在这里讲了,三者的区分就是注意根节点何时遍历。比如前序遍历,就是先遍历根节点,再遍历左右子节点。

这里注意使用递归调用,递归调用的终止条件:节点为空。
递归写法:

//前序遍历以node为根的二分搜索树,递归算法
    void preOrder(TNode<T> *node){
        if(node==nullptr)
            return ;

        std::cout<<node->e;
        preOrder(node->left);
        preOrder(node->right);
    }

非递归写法: 使用栈
1)先将根节点压入栈中,然后取出访问;
2)将右节点(在前)、左节点依次压入栈中,然后再将左节点作为根节点;
3)当栈为空时,访问结束。

Alt

void preOrderNR(){
        std::stack<Node<T> *> stack;
        stack.push(root);
        while (!stack.empty()) {
            TNode<T> *cur = stack.top();
            std::cout << cur->e << " ";
            stack.pop();
            if (cur->right != nullptr)
                stack.push(cur->right);//先压入右节点
            if (cur->left != nullptr)
                stack.push(cur->left);
        }
        std::cout<<std::endl;
    }
3 层序遍历

层序遍历又称为广度优先遍历。层序遍历通常使用非递归方式实现,需要借助队列实现。每次将待遍历的元素存入队列中,再将它的左右子节点排入队列。
Alt

//层序遍历
void levelOrder(){
	std::queue<TNode<T> *> *q=new std::queue<TNode<T> *>();
    q->push(root);
    while (!q->empty()){//队列不为空
		TNode<T> *cur=q->front();
        q->pop();
        std::cout<<cur->e<<" ";
        if(cur->left!= nullptr){
        	q->push(cur->left);//左子节点排入队列中
        }
        if(cur->right!= nullptr){
            q->push(cur->right);//右子节点排入队列中
        }
        std::cout<<std::endl;
    }
}
4 删除元素

1)删除最大元素和最小元素
最小元素,只需要不断遍历树的左子节点,走到底就是最小元素。最大元素就是向右走。

TNode<T> *removeMin(TNode<T> *node){
	if(node->left==nullptr){//向左走,寻找最小元素
		//保存最小元素节点的右子节点
		TNode<T> *rightNode=node->right;
        delete node;//删除最小元素节点
        size--;
        return rightNode;//返回保存的节点
    }
    node->left=removeMin(node->left);
    return node;
}

Alt2)删除仍意元素(Hibbard Deletion)
Alt
Alt
Alt

分别判断节点的情况:
第一,删除只有左孩子的节点;
第二,删除只有右孩子的节点;
第三,删除左右都有孩子的节点。(选择该节点的右子树中最小的值代替该节点)

TNode<T> *remove(TNode<T> *node,T e){
        if(node==nullptr)
            return nullptr;
        if(node->e>e){
            node->left=remove(node->left,e);
            return node;
        }else if(node->e<e){
            node->right=remove(node->right,e);
            return node;
        }
        else{
            if(node->left== nullptr){//左子树为空
                TNode<T> *rightNode=node->right;
                delete node;
                size--;
                return rightNode;
            }
            if(node->right== nullptr){//右子树为空
                TNode<T> *leftNode=node->left;
                delete node;
                size--;
                return leftNode;
            }

            TNode<T> *successor=min(node->right);//获取该节点右子树的最小值
            successor->right=removeMin(node->right);
            successor->left=node->left;

            node->left=node->right= nullptr;
            return successor;
        }
    }
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值