
目录
话不多所,先给各位来一个搜索二叉树:
从这棵树中可以看到这棵树有如下性质:
1.根节点的左节点的值小于根节点的值,根节点的右节点的值大于根节点的值。
2.这棵树的中序遍历的结果是一个升序的数组。
3.这棵树的左子树和右子树都是一颗搜索二叉树。
以上三点便是一棵搜索二叉树的性质!!!
要实现一棵搜索二叉树,首先便要实现它的各个节点。实现如下:
template<class K> struct BSNode { BSNode(const K& key) :_left(nullptr) ,_right(nullptr) ,_key(key) {} BSNode* _left; BSNode* _right; K _key; };这个节点的成员就是它的左指针_left,右指针_right,还有这个节点里包含的一个值_key。
接下来便要开始实现一下这棵树。实现如下:
template<class K> class BSTree { public: private: BSNode* _root; }这棵树的成员便只有一个,那便是_root这个根节点。
在前期工作准备好以后便要来实现我们的方法了,现在来实现我们的插入方法。实现思路如下:
1.因为我们的_root是是私有的,所以我们不能实现一个需要传参的Insert方法。所以它必须是无参的。
2.要实现一个无参的方法,那我们就得套一层_Insert()方法在Insert()方法里面。
3.实现_Insert()方法步骤如下:
1.如果_root是nullptr便new一个节点,让_root接收这个新节点。
2.如果key比我当前的节点值要大,便向右走。
3.如果比我当前的节点值要小,那就向左走。
4.如果走到空(_root的替代值未为nullptr)那就在该位置生成一个新节点,并让该节点的parent的左或者右指针指向这个新节点。
代码如下:
实现无参:
bool Insert( const K& key) { return _Insert(key); }_Insert()方法实现:
bool _Insert(const K& key) { if (_root == nullptr)//若_root是一个nullptr那就给_root new 一个节点 { _root = new BNode(key); return true; } else { BNode* cur = _root; BNode* parent = nullptr; while (cur!=nullptr)//找位置 { parent = cur; if (cur->_key < key) { cur = cur->_right; } else if (cur->_key > key) { cur = cur->_left; } else { return false; } } cur = new BNode(key);//找到后便给这个位置new一个节点 if (key > parent->_key)//判断一下是左边还是右边然后链接 { parent->_right = cur; } else { parent->_left = cur; } return true; } }
查找方法的实现也很简单,其实就是将_Insert()里面的查找代码给复制一份过来便可以了。同样的,我们的查找算法在类的外边也是不能调用_root的,所以也会有封装。Find()函数实现如下:
bool Find(const K& key) { return _Find(key); }我们的_Find()函数实现如下:
bool _Find(const K& key) { BNode* cur = _root; while (cur) { if (key > cur->_key) { cur = cur->_right; } else if (key < cur->_key) { cur = cur->_left; } else { return true; } } return false; }
删除方法的实现大概是最难写的一个代码了,首先我们得先找到这个要删除的节点找到以后分为三种情况讨论:
以如下搜索二叉树为例:
1.要删除节点的左节点为空。如以下情况:
比如要删除10这个节点,我们该如何操作呢?
我们的操作如下:
1.找到我的父亲。
2.判断我是父亲的那个节点。
3.如果我是父亲的左节点便让父亲的左节点连接到我的右节点上。如果我是父亲右节点,便让父亲的右节点指向我的右节点。
但是这里需要注意一个点:如果我是root,我便没有父亲。如以下情况:
在这种情况下我们便可以直接让右节点担任root节点:
if (cur == _root) { _root = _root->_right; }左节点为空的情况下实现删除代码如下:
if (cur->_left == nullptr) { if (cur == _root) { _root = _root->_right; } else { if (cur->_key > parent->_key) { parent->_right = cur->_right; } else { parent->_left = cur->_right; } } return true; }2.要删除的节点的右节点为空。
其实这种情况下的的代码的值和前面的实现逻辑是一样的,所以不解释直接给出实现代码:
else if (cur->_right == nullptr) { if (cur == _root) { _root = cur->_left; } else { if (cur->_key > parent->_key) { parent->_right = cur->_left; } else { parent->_left = cur->_left; } } return true; }3.当我要删除的节点的左右两个节点都在
这个删除便是我们这个删除方法里面最难实现的一个代码,在这里我们要使用替换法来删除:
1.如何使用替换法呢?
步骤:1.定义parent和subLeft,subLeft定义为cur->right。
记录我的父亲,这个父亲要初始化cur(当前节点)。
2.找到当前节点的右子树的最左节点subLeft并更新parent位subLeft的父亲节点。
3.交换cur节点和subLeft两个节点的值(使用swap).
4.链接。
了解完以上步骤以后写下如下代码:
else {//有两个孩子,替换法。(找右子树的最左节点) BNode* Parent = cur; BNode* SubLeft = cur->_right; while (SubLeft->_left) { Parent = SubLeft; SubLeft = SubLeft->_left; } swap(cur->_key, SubLeft->_key); if (SubLeft == Parent->_left) { Parent->_left = SubLeft->_left; } else { Parent->_right = SubLeft->_left; } return true; }在这里解释一下:
1.为什么parent要初始为cur,如以下例子:
假如是以上的情况,那我的这段代码是不会进去的:
while (SubLeft->_left) { Parent = SubLeft; SubLeft = SubLeft->_left; }那如果我的parent 如果赋值为nullptr的话,那便会解引用nullptr:
if (SubLeft == Parent->_left) { Parent->_left = SubLeft->_left; } else { Parent->_right = SubLeft->_left; }所以我们必须要将parent初始化为cur。这个时候也能删除。
2.链接该如何连接?
我实现的连接代码是这样的:
if (SubLeft == Parent->_left) { Parent->_left = SubLeft->_left; } else { Parent->_right = SubLeft->_left; }在这里我们首先得先判断一下我们的subLeft是我的parent节点的哪一位?
可能是右节点:
就像我们上面的删除8的情况一样,我要删除的是根节点,我的根节点的左节点是nullptr。
也可能是左节点:
我进入了循环:
while (SubLeft->_left) { Parent = SubLeft; SubLeft = SubLeft->_left; }
- #include
- using namespace std;
- #include
-
-
- template<class K>
- struct BNode
- {
- BNode(const K& key)
- :_key(key)
- ,_left(nullptr)
- ,_right(nullptr)
- {}
-
- K _key;
- BNode
* _left; - BNode
* _right; - };
-
-
- template<class K>
- class BSTree
- {
- public:
-
- bool Insert( const K& key)
- {
- return _Insert(key);
- }
-
- void Inorder()
- {
- _Inorder(_root);
- cout << endl;
- }
-
- bool Find(const K& key)
- {
- return _Find(key);
- }
-
- bool Erase(const K& key)
- {
- return _Erase(key);
- }
-
- private:
- bool _Insert(const K& key)
- {
- if (_root == nullptr)
- {
- _root = new BNode
(key); - return true;
- }
- else
- {
- BNode
* cur = _root; - BNode
* parent = nullptr; -
- while (cur!=nullptr)
- {
- parent = cur;
- if (cur->_key < key)
- {
- cur = cur->_right;
- }
- else if (cur->_key > key)
- {
- cur = cur->_left;
- }
- else
- {
- return false;
- }
- }
-
- cur = new BNode
(key); - if (key > parent->_key)
- {
- parent->_right = cur;
- }
- else
- {
- parent->_left = cur;
- }
- return true;
- }
-
- }
-
- bool _Erase(const K& key)
- {
- assert(_root);
- BNode
* cur = _root; - BNode
* parent = cur; -
-
- while (cur)
- {
- if (key > cur->_key)
- {
- parent = cur;
- cur = cur->_right;
- }
-
- else if (key < cur->_key)
- {
- parent = cur;
- cur = cur->_left;
- }
- else
- {
- if (cur->_left == nullptr)
- {
- if (cur == _root)
- {
- _root = _root->_right;
- }
- else
- {
- if (cur->_key > parent->_key)
- {
- parent->_right = cur->_right;
- }
- else
- {
- parent->_left = cur->_right;
- }
- }
- return true;
- }
-
- else if (cur->_right == nullptr)
- {
- if (cur == _root)
- {
- _root = cur->_left;
- }
- else
- {
- if (cur->_key > parent->_key)
- {
- parent->_right = cur->_left;
- }
- else
- {
- parent->_left = cur->_left;
- }
- }
- return true;
- }
-
- else
- {//有两个孩子,替换法。(找右子树的最左节点)
- BNode
* Parent = cur; - BNode
* SubLeft = cur->_right; -
- while (SubLeft->_left)
- {
- Parent = SubLeft;
- SubLeft = SubLeft->_left;
- }
-
- swap(cur->_key, SubLeft->_key);
-
- if (SubLeft == Parent->_left)
- {
- Parent->_left = SubLeft->_left;
- }
- else
- {
- Parent->_right = SubLeft->_left;
- }
-
- return true;
- }
- }
- }
-
- return false;
-
-
-
- }
-
- bool _Find(const K& key)
- {
- BNode
* cur = _root; - while (cur)
- {
- if (key > cur->_key)
- {
- cur = cur->_right;
- }
-
- else if (key < cur->_key)
- {
- cur = cur->_left;
- }
- else
- {
- return true;
- }
- }
-
- return false;
- }
-
- void _Inorder(BNode
* root) - {
- if (root == nullptr)
- {
- return;
- }
-
- _Inorder(root->_left);
- cout << root->_key << " ";
- _Inorder(root->_right);
- }
-
-
-
- BNode
*_root =nullptr ; - };
实际上还可以实现一个递归版本的二叉搜索树,有时间再写。