二叉搜索树(二叉排序树、二叉查找树)

  • 二叉搜索树
  • 特点
  • 操作
  • 代码实现

1. 二叉搜索树

二叉搜索树又称二叉排序树/二叉查找树,它或者是一棵空树,或者是具有以下性质的二叉树:

  • 若它的左子树不为空,则左子树上所有节点的值都小于根节点的值。
  • 若它的右子树不为空,则右子树上所有节点的值都大于根节点的值。
  • 它的左右子树也分别为二叉搜索树

2. 特点

二叉搜索树的中序遍历有序。

3 . 操作

3.1 查找数据:

判断要查找的数据是否等于当前节点的值(初始值:cur=_root)

i. 若等于,返回当前节点;

ii. 若小于,向左子树进行查找;(cur=cur->_left)

iii. 若大于,向右子树进行查找。(cur=cur->_right)

iv. 若未找到该数据,则返回nullptr

3.2 插入数据

在二叉树的叶子节点插入新数据(不能插入重复的数据,若插入重复数据,则直接返回错误)。

i. 判断树是否为空,若为空,创建新节点,让根节点指向创建的该节点,返回true;

ii. 若树不为空。

  1. 找合适的位置(并保留其父节点指针,用于插入);
  2. 若该元素已存在,则返回false;
  3. 否则,创建新节点;
  4. 插入到其父节点的左孩子或者右孩子(判断该节点的值和父节点值的大小,以确定其为父节点的左孩子/右孩子)。

3.3 删除数据

i. 找位置(并找其父节点)

ii. 找到节点,删除。

  1. 叶子节点(找父节点,直接删除,父节点对应位置(右/左)置空。)
    1) 要删节点为根节点,删除根节点,将根节点置空;
    2) 判断待删除节点为其父节点的左孩子还是右孩子
  2. 单边节点(找父节点,父节点对应位置连接其左子树/右子树,删除查找的节点)
    1) 若要删节点为根节点,将根节点重新指向要删除节点的左子树/右子树,删除待删除节点;
    2) 否则,判断待删除节点为其父节点的左孩子还是右孩子,将其父节点对应位置与其左子树/右子树相连接,删除待删除节点。
    3)返回true
  3. 既有左子树,也有右子树(找左子树的最右节点,与要删除节点进行数值交换,转换为删除叶子结点)
    1) 找左子树最右节点,并记录其父节点
    2) 交换(待删除节点和左子树最右节点的值)
    3) 连接父节点,删除交换后的左子树最右节点。

3.4 中序遍历

核心步骤思路:

  1. 递归访问左子树;
  2. 访问节点内容;
  3. 递归访问右子树。

4. C++代码实现

#pragma once
#include<iostream>
using namespace std;

template<class K,class V>
struct BNode {
    K _key;
    V _value;
    BNode* _left;
    BNode* _right;
    BNode(const K& key,const V& value) :_key(key),_value(value), _left(nullptr), _right(nullptr) {}
};

template<class K,class V>
class BTree {

public:
    typedef BNode<K,V> Node;

    Node* find(const K& key)
    {

        Node* cur = _root;
        while (cur)
        {
            if (cur->_key == key)
                return cur;
            else if (cur->_key > key)
                cur = cur->_left;
            else
                cur = cur->right;
        }
        return nullptr;
    }

    BTree()
        :_root(nullptr)
    {}

    //拷贝二叉树的数据和结构
    Node* copy(Node* root)                      //创建自上而下,连接自下而上
    {
        if (root == nullptr)
            return nullptr;
        Node* newNode = new Node(root->_key,root->_value);
        newNode->_left = copy(root->_left);
        newNode->_right = copy(root->_right);
        return newNode;
    }
    BTree(const BTree<K,V>& btree)
        :_root(copy(btree._root))
    {}

    //不插入重复的值,每次插入到叶子节点
    bool insert(const K& key,const V& value)
    {
        if (_root == nullptr)
        {
            _root = new Node(key,value);
            return true;
        }
        //搜索,找到一个合适的位置插入
        Node* cur = _root;
        Node* parent = nullptr;
        while (cur)
        {
            parent = cur;
            if (cur->_key == key)
                return false;
            else if (cur->_key > key)
                cur = cur->_left;
            else
                cur = cur->_right;
        }

        //插入
        cur = new Node(key,value);
        if (parent->_key > key)
            parent->_left = cur;
        else
            parent->_right = cur;
        return true;
    }
    void inorder()
    {
        _inorder(_root);
    }

    void _inorder(Node* root)
    {
        if (root)
        {
            _inorder(root->_left);
            cout <<" key:"<<root->_key << "——value:"<<root->_value<<'\t';
            _inorder(root->_right);
        }
        cout << endl;
    }
    void destroy(Node* root)
    {
        if (root)
        {
            destroy(root->_left);
            destroy(root->_right);
            cout << "destroy: " << root->_key << "——" << root->_value << endl;
            delete root;
        }
    }

    ~BTree()
    {
        if (_root)
        {
            destroy(_root);
            _root = nullptr;
        }
    }
    bool erase(const K& key)
    {
        //查找该节点
        Node* cur = _root;
        Node* parent = nullptr;
        while (cur)
        {
            if (cur->_key == key)
                break;
            parent = cur;
            if (cur->_key > key)
                cur = cur->_left;
            else
                cur = cur->_right;
        }
        //判断需要删除的节点是否存在
        if (cur == nullptr)
            return false;

        //删除
        //1.要删除节点为叶子节点
        if (cur->_left == nullptr && cur->_right == nullptr)
        {
            //判断要删除节点是否为根节点
            if (cur == _root)
            {
                _root = nullptr;
            }

            //判断要删除的节点属于父节点的左孩子还是右孩子,并将对应的父节点子节点置为空
            else
            {
                if (parent->_left == cur)
                    parent->_left = nullptr;
                else
                    parent->_right = nullptr;
            }
            delete cur;
        }
        //2.非叶子节点,且当前节点有一个子树,另一个子树为空
        //左子树为空,判断当前节点为父节点的左子树还是右子树(若为父节点的左子树,则将其右子树连接到父节点的左子树,若为父节点的右子树,则将其右子树连接到父节点的右子树)
        else if (cur->_left == nullptr)
        {
            //判断当前节点是否为根节点
            if (cur == _root)
                _root = cur->_right;          //根节点
            else
            {
                if (parent->_left == cur)
                    parent->_left = cur->_right;
                else
                    parent->_right = cur->_right;
            }

            //删除节点
            delete cur;
        }
        else if (cur->_right == nullptr)
        {
            //判断当前节点是否为根节点
            if (cur == _root)
                _root = cur->_left;
            else
            {
                if (parent->_left == cur)
                    parent->_left = cur->_left;
                else
                    parent->_right = cur->_left;
            }
            delete cur;
        }
        //3.要删除节点左右子树均不为空。当前位置的新的替代节点,一定为左子树的最大节点(左子树最右节点),或者为右子树的最小节点(右子树最左节点),。
        //① 找左子树的最右节点(右子树的最左节点)
        //② 交换待删除节点与左子树的最右节点(或者右子树的最左节点)的值
        //③ 然后删除交换后的叶子结点
        else
        {
            //①找左子树的最右节点
            Node* leftMostRight = cur->_left;
            parent = cur;

            while (leftMostRight->_right)
            {
                parent = leftMostRight;
                leftMostRight = leftMostRight->_right;
            }
            //②交换
            swap(cur->_key, leftMostRight->_key);
            swap(cur->_value, leftMostRight->_value);

            //③连接,删除
            if (parent->_left == leftMostRight)
                parent->_left = leftMostRight->_left;
            else
                parent->_right = leftMostRight->_left;
            delete leftMostRight;
        }
    }
private:
    Node* _root;
};
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值