浅谈二叉搜索树

二叉搜索树的特点
对于二叉搜索树来说,每一个节点都存有一个关键字(key)用来区分不同的节点,每一个节点的key值各不相同。根据人们的需要,在我们可以同时放进一个value类型,来表示key的关键字所存的内容。这就和字典的功能比较相似,也就是俗称的KV。
1.左子树上所有节点的key值都小于根节点。
2.右子树上所有节点的key值都大于根节点。
从这里可知,二叉搜索树的key值能存放的类型必须是可以比较大小的类型。

这样的一棵树的特点是,如果用中序遍历这棵树,得到的将会是一组有序数组。
这里写图片描述

二叉搜索树的增删查改
对于一棵二叉搜索树的增删查改,首先改是不被允许的,因为一个节点的数据被修改了,这颗树很可能就不是二叉搜索树了,会改变这棵树原有的结构,这对于之后的AVL树以及红黑树都是一样的。

下面我们先来看一下它的增。
对于增来说,很简单,我们只要从根节点开始比较要插入节点的key值与当前节点cur的key值就可以了,如果根节点为空,则在_root插入该节点,若不为空,并且cur的key值比key值小,则往cur的右子树走,反之则往cur的左子树走,直到找到一个为空的节点插入该节点。如上图要插入key值为10的节点,则从根节点起,不断地走它的右子树,直到key值为9的右子树为空,则插入10。
这里写图片描述

对于找来说,也是一样的。从根节点起开始找,比较要找的key值与当前cur的key值的大小,cur的key比key小,则往右子树走,反之则往左子树走。若找到了该节点,则返回该节点,若找不到则返回NULL。

最难实现的二叉搜索树的删除。因为要删除一个节点,就会改变整棵树的形状,但我们又不想将这颗树的形状改变太多,怎么办呢?这里我们采用替换删除法。比如我们要删除上图的5,那么我们可以发现,如果我们把5删除,把6移到5的位置,整棵树依旧是二叉搜索树,且它的形状基本没有怎么变。把4移到5的位置效果是一样的。所以我们就可以用替换法来进行删除,不难发现的是,6这个节点是5的右树的最左节点(即中序遍历5的右子树的第一个节点),4这个节点是5的左树的最右节点(即中序遍历5的左子树的最后一个节点),因为两者效果是一样的,这里我们采用用左树的最右的节点的替换方法。
这里写图片描述
接下来我们分析要删除的节点的可能的情况(相对于第一张图),总的来说有以下三种情况:
1.左子树为空(如8节点)
2.右子树为空
3.左右子树都不为空(如3、5、7节点)
对于叶子节点来说,它既可以看成是左为空,也可以看成是右为空,这里不做特殊处理。

1.首先对于左子树为空的节点。我们在找到这个节点的过程中,同时记下它的父亲节点parent,如果cur是parent的左,则将cur的右给parent的左,反之则将cur的右给parent的右(如果parent为空,则将cur的右给_root)。
2.右子树为空与左子树为空一样,无非就是将上面cur的右变为cur的左,思想完全一样。
3.对于左右都不为空的情况,这时我们就要找到它右树的最左节点subLeft,在找subLeft的过程中,记下subLeft的父亲parent,把subLeft的key值赋给cur的key,如果subLeft是parent的右,则将subLeft的右赋给parent的右,反之则将subLeft的右赋给parent的左,然后将这个节点直接删除。

具体实现的代码如下:

#pragma once 

#include <iostream>
using namespace std;

template <class K,class V>
struct BinarySearchTreeNode
{
    K _key;
    V _value;

    BinarySearchTreeNode<K, V>* _left;
    BinarySearchTreeNode<K, V>* _right;

    BinarySearchTreeNode(const K& key, const V& value)
        : _key(key)
        , _value(value)
        , _left(NULL)
        , _right(NULL)
    {}
};

template <class K,class V>
class BinarySearchTree
{
    typedef BinarySearchTreeNode<K, V> Node;
public:
    BinarySearchTree()
        :_root(NULL)
    {}

    bool Insert(const K& key, const V& value)
    {
        if (_root == NULL)
        {
            _root = new Node(key, value);
            return true;
        }

        Node* cur = _root;
        Node* parent = NULL;
        while (cur)
        {
            if (key > cur->_key)
            {
                parent = cur;
                cur = cur->_right;
            }
            else if (key < cur->_key)
            {
                parent = cur;
                cur = cur->_left;
            }
            else
            {
                return false;
            }
        }

        cur = new Node(key, value);
        if (cur->_key < parent->_key)
        {
            parent->_left = cur;
        }
        else
        {
            parent->_right = cur;
        }
        return true;
    }

    bool Remove(const K& key)
    {
        if (_root == NULL)
        {
            return false;
        }

        Node* parent = NULL;
        Node* cur = _root;

        while (cur)
        {
            if (key > cur->_key)
            {
                parent = cur;
                cur = cur->_right;
            }
            else if (key < cur->_key)
            {
                parent = cur;
                cur = cur->_left;
            }
            else
            {
                Node* del = cur;
                //1.左为空 2.右为空 3.左右都不为空
                if (cur->_left == NULL)
                    //左为空
                {
                    if (parent == NULL)
                        //如果cur是根节点
                    {
                        _root = cur->_right;
                    }
                    else
                    {
                        if (cur == parent->_left)
                        {
                            parent->_left = cur->_right;
                        }
                        else
                        {
                            parent->_right = cur->_right;
                        }
                    }
                }
                else if (cur->_right == NULL)
                    //右为空
                {
                    if (parent == NULL)
                        //如果cur是根节点
                    {
                        _root = cur->_left;
                    }
                    else
                    {
                        if (cur = parent->_left)
                        {
                            parent->_left = cur->_left;
                        }
                        else
                        {
                            parent->_right = cur->_left;
                        }
                    }
                }
                else
                    //左右都不为空
                {
                    Node* parent = cur;
                    Node* subLeft = cur->_right;
                    while (subLeft->_left)
                    {
                        parent = subLeft;
                        subLeft = subLeft->_left;
                    }
                    del = subLeft;
                    cur->_key = subLeft->_key;
                    if (parent->_left == subLeft)
                    {
                        parent->_left = subLeft->_right;
                    }
                    else
                    {
                        parent->_right = subLeft->_right;
                    }
                }
                delete del;
                return true;
            }
        }
        return false;
    }


    Node* Find(const K& key)
    {
        if (_root == NULL)
        {
            return NULL;
        }

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

        return NULL;
    }

    void Inorder()
    {
        _Inorder(_root);
        cout << endl;
    }

protected:
    bool _Inorder(Node* root)
    {
        if (root == NULL)
        {
            return false;
        }

        _Inorder(root->_left);
        cout << root->_key << " ";
        _Inorder(root->_right);

        return true;
    }
private:
    Node* _root;
};

void TestBinarySearchTree()
{
    int a[] = { 5, 3, 4, 1, 7, 8, 2, 6, 0, 9 };
    BinarySearchTree<int, int> t;
    for (size_t i = 0; i < sizeof(a) / sizeof(int); ++i)
    {
        t.Insert(a[i], i);
    }
    t.Inorder();
    t.Remove(8);
    t.Inorder();
    t.Remove(0);
    t.Inorder();
    t.Remove(1);
    t.Inorder();
    t.Remove(3);
    t.Inorder();
    t.Remove(2);
    t.Inorder();
    t.Remove(9);
    t.Inorder();
    t.Remove(5);
    t.Inorder();
    t.Remove(4);
    t.Inorder();
    t.Remove(7);
    t.Inorder();
    t.Remove(6);
    t.Inorder();
}

测试是随机删除的,每删一次打印一次,测试结果如下:
这里写图片描述

以上的增删查都是非递归的,接下来来看下递归的代码实现,因为核心思想是类似的,只是多了基层递归调用。不同的地方是,我们在传值的时候用了一点巧妙的手段,拿_RemoveR来说,我们传了一个节点的引用。这有什么用呢?因为在删除的时候,我们需要知道当前cur的parent,然而函数的返回值不可能既返回当前节点,又返回当前节点的父亲的,只能返回一个。
这里写图片描述
比如我们删8的时候,我们需要知道8的父亲7,把8的孩子9连到7上。这是我们给函数传一个(Node*& root),我们删8的时候,root的值是8,但同时,root也是7这个节点的别名,这样我们就巧妙地记下了当前节点的父亲节点。

#pragma once 

#include <iostream>
using namespace std;

template <class K,class V>
struct BinarySearchTreeNode
{
    K _key;
    V _value;

    BinarySearchTreeNode<K, V>* _left;
    BinarySearchTreeNode<K, V>* _right;

    BinarySearchTreeNode(const K& key, const V& value)
        : _key(key)
        , _value(value)
        , _left(NULL)
        , _right(NULL)
    {}
};

template <class K,class V>
class BinarySearchTree
{
    typedef BinarySearchTreeNode<K, V> Node;
public:
    BinarySearchTree()
        :_root(NULL)
    {}

    bool InsertR(const K& key, const V& value)
    {
        return _InsertR(_root, key, value);
    }

    bool _InsertR(Node*& root, const K& key, const V& value)
    {
        if (root == NULL)
        {
            root = new Node(key, value);
            return true;
        }

        if (root->_key < key)
        {
            return _InsertR(root->_right, key, value);
        }
        else if (root->_key>key)
        {
            return _InsertR(root->_left, key, value);
        }
        else
        {
            return false;
        }
    }

    bool RemoveR(const K& key)//递归删除
    {
        return _RemoveR(_root, key);
    }

    bool _RemoveR(Node*& root, const K& key)
    {
        if (root == NULL)
        {
            return false;
        }

        if (root->_key < key)
        {
            return _RemoveR(root->_right, key);
        }
        else if (root->_key > key)
        {
            return _RemoveR(root->_left, key);
        }
        else 
        {
            Node* del = root;
            if (root->_left == NULL)
            //左为空
            {
                root = root->_right;
            }
            //右为空
            else if (root->_right == NULL)
            {
                root = root->_left;
            }
            else
            //左右都不为空
            {
                Node* parent = root;
                Node* subLeft = root->_right;
                while (subLeft->_left)
                {
                    parent = subLeft;
                    subLeft = subLeft->_left;
                }

                del = subLeft;
                root->_key = subLeft->_key;

                if (parent->_left == subLeft)
                {
                    parent->_left = subLeft->_right;
                }
                else
                {
                    parent->_right = subLeft->_right;
                }
            }

            delete del;
            return true;
        }
    }

    const Node* FindR(const K& key)
    {
        return(_root, key);
    }

    const Node* _FindR(Node* root,const K& key)
    {
        if (root == NULL)
        {
            return NULL;
        }

        if (root->_key < key)
        {
            return _FindR(root->_right, key);
        }
        else if (root->_key < key)
        {
            return _FindR(root->_left, key);
        }
        else
        {
            return root;
        }
    }

    void Inorder()
    {
        _Inorder(_root);
        cout << endl;
    }

protected:
    bool _Inorder(Node* root)
    {
        if (root == NULL)
        {
            return false;
        }

        _Inorder(root->_left);
        cout << root->_key << " ";
        _Inorder(root->_right);

        return true;
    }
private:
    Node* _root;
};

void TestBinarySearchTree()
{
    int a[] = { 5, 3, 4, 1, 7, 8, 2, 6, 0, 9 };
    BinarySearchTree<int, int> t;
    for (size_t i = 0; i < sizeof(a) / sizeof(int); ++i)
    {
    t.InsertR(a[i], i);
    }
    t.RemoveR(8);
    t.Inorder();
    t.RemoveR(0);
    t.Inorder();
    t.RemoveR(1);
    t.Inorder();
    t.RemoveR(3);
    t.Inorder();
    t.RemoveR(2);
    t.Inorder();
    t.RemoveR(9);
    t.Inorder();
    t.RemoveR(5);
    t.Inorder();
    t.RemoveR(4);
    t.Inorder();
    t.RemoveR(7);
    t.Inorder();
    t.RemoveR(6);
    t.Inorder();
}

测试结果也是一样的
这里写图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值