什么是二叉搜索树
二叉搜索树的定义
是一颗二叉树的所有节点满足:根的左右孩子存在时,满足 左孩子 < 根 < 右孩子
递归定义
则是:
左子树的根
< 根
< 右子树的根
左子树是二叉搜索树
,右子树是二叉搜索树
写一个验证二叉搜索树的函数
Leetecode题目链接🔗
封装一个二叉树类
文件布置
BSTree.h
用于声明和实现BSTree
类
test.cpp
用于测试
头文件
BSTree.h
1 2 3
| #include <iostream> #include <utility>
|
test.cpp
命名空间
这里使用key_value
作为命名空间,表示这是键值表示的搜索二叉树
定义节点
二叉树的节点用于储存键值对
和左右指针
,并提供默认构造函数
,使用初始化列表
初始化成员变量
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| template<class K,class V> class BSTNode { public: BSTNode(const K& key = K(), const V& value = V()) :_left(nullptr) , _right(nullptr) , _key(key) , _value(value) {} BSTNode<K, V>* _left; BSTNode<K, V>* _right; K _key; V _value; };
|
封装二叉搜索树
Binary Search Tree
,这里用简称BSTree
封装
基本架构
鉴于该类的接口基本依赖于成员变量,所以先组织好成员变量
作为一颗二叉树类
,成员变量仅需一个指向根的指
针即可
再次之前先用typedef
定义一个节点类
出来用于简化代码
最后提供一个默认构造函数
将_root
初始化为nullptr
1 2 3 4 5 6 7 8 9
| template<class K,class V> class BSTree { typedef BSTNode<K, V> Node; public: BSTree() :_root(nullptr) {} private: Node* _root; };
|
insert
函数
准备好后第一个接口就是insert
,用于构建搜索二叉树
这里需要考虑的情况有
- 空树时的插入
- 插入的
key
已存在
- 一般情况下成功的插入
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33
| public: 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) { if (key < cur->_key) { parent = cur; cur = cur->_left; } else if (key > cur->_key) { parent = cur; cur = cur->_right; } else { return false; } } if (key < parent->_key)parent->_left = new Node(key, value); else parent->_right = new Node(key, value); return true; }
|
in_order
函数
使用此函数前序遍历打印
二叉树来验证其满足搜索树
的性质
这里使用递归打印,所以要借助_in_order
子函数
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| public: void in_order() { _in_order(_root); std::cout << std::endl; } protected: void _in_order(Node* root) { if (root == nullptr) return; _in_order(root->_left); std::cout << root->_value << " " << std::endl; _in_order(root->_right); }
|
然后写一段测试代码测试性质
test.cpp
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| #include "BSTree.h" #include <vector> using namespace std; int main() { vector<int> arr({ 2,3,1,5,4,6,8,7 }); key_value::BSTree<int, int> bst; for (int i = 0; i < arr.size(); ++i) { bst.insert(arr[i],arr[i]); }
bst.in_order();
return 0; }
|
find
函数
可以用find
函数查找对应key
的结点。
同时观察可知,控制cur
和parent
的移动的代码段和前面的函数很像,所以给find
函数分出来一个_find
子函数,并使它返回pair<Node*,Node*>
,将这两个指针返回利于其它函数对_find
的回调
同时为了简化代码,继续用typedef
封装类型
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34
| public: typedef std::pair< Node*, Node* > PNN; bool find(const K& key) { return _find(key).first != nullptr; } protected:
PNN _find(const K& key) { if (_root == nullptr) return {nullptr,nullptr}; Node* cur = _root; Node* parent = nullptr; while (cur) { if (key < cur->_key) { parent = cur; cur = cur->_left; } else if (key > cur->_key) { parent = cur; cur = cur->_right; } else { return { cur,parent }; } } return { nullptr,parent }; }
|
重写insert
函数
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
| bool insert(const K& key, const V& value) { if (_root == nullptr) { _root = new Node(key, value); return true; } PNN pnn = _find(key);
Node* cur = pnn.first; Node* parent = pnn.second;
if (cur != nullptr) { return false; } if (key < parent->_key)parent->_left = new Node(key, value); else parent->_right = new Node(key, value); return true; }
|
erase
函数
这里也可以复用_find
来方便地删除结点
这里要考虑的情况有:
- 树为空
- 删除最后一个结点
- 删除根节点
- 左子树为空(包括叶子结点)
- 右子树为空
- 删除一般的结点
当树有>=2
个结点,且要删除非叶子
结点时,要考虑结点替换
,否则二叉树会断掉,这里一般两种策略,取左子树的最右结点(最大结点),或取右子树的最左结点(最小结点)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72
| public: bool erase(const K& key) { if (_root == nullptr)return false; if (_root->_key == key && _root->_left == nullptr && _root->_right == nullptr) { delete _root; _root = nullptr; } PNN pnn = _find(key); Node* cur = pnn.first; Node* parent = pnn.second; if (cur == nullptr) return false;
if(cur->_left == nullptr) { if (cur == _root) { Node* right = _root->_right; delete _root; _root = right; } if (cur == parent->_left) parent->_left = cur->_right; else parent->_right = cur->_right; delete cur; } else if (cur->_right == nullptr) { if (cur == _root) { Node* left = _root->_left; delete _root; _root = left; } if (cur == parent->_left)parent->_left = cur->_left; else parent->_right = cur->_left; } else { Node* _cur = cur->_left; Node* _parent = cur; while (_cur->_right != nullptr) { _parent = _cur; _cur = _cur->_right; } if (_cur == _parent->_left) _parent->_left = nullptr; else _parent->_right = nullptr; _cur->_left = cur->_left; _cur->_right = cur->_right; if (cur == _root) { delete _root; _root = _cur; } else { if (cur == parent->_left) parent->_left = _cur; else parent->_right = _cur; delete cur; }
} return true; }
|
拷贝构造
利用二叉树的性质,可以再构建个copy
子函数来递归拷贝
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| public: BSTree(const BSTree<K>& t) { _root = Copy(t._root); } protected: Node* copy(Node* root) { if (root == nullptr) return nullptr; Node* pnode = new Node(root->_key, root->_value); pnode->_left = copy(root->_left); pnode->_right = copy(root->_right); return pnode; }
|
析构函数
这里也用destroy
子函数来递归地后序遍历依次删除各个结点
1 2 3 4 5 6 7 8 9 10 11 12 13
| public: ~BSTree() { destroy(_root); } protected: void destroy(Node* root) { if (root == nullptr) return; destroy(root->_left); destroy(root->_right); delete root; }
|
至此,一个基本的二叉搜索树已封装完成
实现的功能有
- 构建二叉搜索树
- 拷贝复制二叉树
- 按
key
值查找
- 按
key
值删除
完整代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209
| #pragma once #include <iostream> #include <utility>
namespace key_value { template<class K,class V> class BSTNode { public: BSTNode(const K& key = K(), const V& value = V()) :_left(nullptr) , _right(nullptr) , _key(key) , _value(value) {} BSTNode<K, V>* _left; BSTNode<K, V>* _right; K _key; V _value; };
template<class K,class V> class BSTree { typedef BSTNode<K, V> Node; public: BSTree() :_root(nullptr) {}
BSTree(const BSTree<K, V>& bst) { _root = copy(bst._root); }
~BSTree() { destroy(_root); }
bool insert(const K& key, const V& value) { if (_root == nullptr) { _root = new Node(key, value); return true; } PNN pnn = _find(key);
Node* cur = pnn.first; Node* parent = pnn.second;
if (cur != nullptr) { return false; } if (key < parent->_key)parent->_left = new Node(key, value); else parent->_right = new Node(key, value); return true; }
void in_order() { _in_order(_root); std::cout << std::endl; } protected: void _in_order(Node* root) { if (root == nullptr) return; _in_order(root->_left); std::cout << root->_value << " "; _in_order(root->_right); }
public: typedef std::pair< Node*, Node* > PNN; bool find(const K& key) { return _find(key).first != nullptr; } protected: PNN _find(const K& key) { if (_root == nullptr) return {nullptr,nullptr}; Node* cur = _root; Node* parent = nullptr; while (cur) { if (key < cur->_key) { parent = cur; cur = cur->_left; } else if (key > cur->_key) { parent = cur; cur = cur->_right; } else { return { cur,parent }; } } return { nullptr,parent }; } public:
bool erase(const K& key) { if (_root == nullptr)return false; if (_root->_key == key && _root->_left == nullptr && _root->_right == nullptr) { delete _root; _root = nullptr; } PNN pnn = _find(key); Node* cur = pnn.first; Node* parent = pnn.second; if (cur == nullptr) return false;
if(cur->_left == nullptr) { if (cur == _root) { Node* right = _root->_right; delete _root; _root = right; } if (cur == parent->_left) parent->_left = cur->_right; else parent->_right = cur->_right; delete cur; } else if (cur->_right == nullptr) { if (cur == _root) { Node* left = _root->_left; delete _root; _root = left; } if (cur == parent->_left)parent->_left = cur->_left; else parent->_right = cur->_left; } else { Node* _cur = cur->_left; Node* _parent = cur; while (_cur->_right != nullptr) { _parent = _cur; _cur = _cur->_right; } if (_cur == _parent->_left) _parent->_left = nullptr; else _parent->_right = nullptr; _cur->_left = cur->_left; _cur->_right = cur->_right; if (cur == _root) { delete _root; _root = _cur; } else { if (cur == parent->_left) parent->_left = _cur; else parent->_right = _cur; delete cur; }
} return true; }
protected: Node* copy(Node* root) { if (root == nullptr) return nullptr; Node* pnode = new Node(root->_key, root->_value); pnode->_left = copy(root->_left); pnode->_right = copy(root->_right); return pnode; }
void destroy(Node* root) { if (root == nullptr) return; destroy(root->_left); destroy(root->_right); delete root; } private: Node* _root; }; }
|