引言
首先笔者知道就是一般来看原理的同学很少可以有时间将我的原理看完,其实很多人就是想看写代码,因为我也是这样,所以我这里直接提供一份我写的搜索二叉树的模版,要用的自取,或者也可以来笔者的github的DataStructure上自取github账号
namespace mycode {
enum PosfromBSTree {
left,
right,
};
template<class T,class V>//这里改成了key / value 模型
struct BSTreeNode {
T _val;
V _value;
BSTreeNode<T,V>* _left;
BSTreeNode<T,V>* _right;
BSTreeNode(const T& val = T(),const V& value = V(),BSTreeNode<T,V>* left = nullptr,BSTreeNode<T,V>* right = nullptr):\
_value(value),_val(val),_left(left),_right(right){};
};
template<class T,class V,class Compare = std::less<T>>
class BSTree {
public:
typedef BSTreeNode<T,V> Node;
explicit BSTree():_root(nullptr),_size(0){}
Node* getRoot() { return _root; }
void clear(Node* root);//这里为什么要单独调用getRoot(),这是因为这里还来不及调用this指针
bool push(const T& val,const V& va);
bool pop(const T& val);
size_t size() const { return _size; }
bool empty() const { return _root == nullptr; }
Node* Find(const T& src);
void inOrder(Node* root) const {
if(root == nullptr) return;
inOrder(root->_left);
std::cout << root->_val << " ";
inOrder(root->_right);
}
bool erase(const T& val);
~BSTree(){ clear(_root); _root = nullptr; _size = 0; }
private:
Node* _root;
size_t _size;
Compare cmp;//创建仿函数可以方便整个类进行调用
};
template<class T,class V,class Compare>
void BSTree<T,V,Compare>::clear(Node* root) {
if(root == nullptr) return;
clear(root->_left);
clear(root->_right);
delete root;
root = nullptr;
}
template<class T,class V,class Compare>
bool BSTree<T,V,Compare>::push(const T& val,const V& va){
::mycode::PosfromBSTree position = ::mycode::PosfromBSTree::left;
Node* parent = nullptr;
Node* cur = _root;
if(cur == nullptr) {
_root = new Node(val);
return true;
}
while(cur) {
if(cmp(val,cur->_val)) {
parent = cur;
cur = cur->_left;
position = ::mycode::PosfromBSTree::left;
}else if(cmp(cur->_val,val)) {
parent = cur;
cur = cur->_right;
position = ::mycode::PosfromBSTree::right;
}
else return false;
}
cur = new Node(val,va);
if(position == ::mycode::PosfromBSTree::left) parent->_left = cur;
else parent->_right = cur;
_size++;
return true;
}
template<class T,class V,class Compare>
bool BSTree<T,V,Compare>::pop(const T& val){
Node* parent = nullptr;
Node* cur = _root;
if(cur == nullptr) return false;
while(cur) {
if(cmp(val,cur->_val)) {
parent = cur;
cur = cur->_left;
}
else if(cmp(cur->_val,val)) {
parent = cur;
cur = cur->_right;
}
else {
if(cur == nullptr) return false;//没找到直接返回false
//找到节点
//0个或者一个孩子的情况可以统一考虑
if(cur->_left == nullptr) {
if(parent == nullptr) _root = cur->_right;
else {
if(cur == parent->_left) parent->_left = cur->_right;
else parent->_right = cur->_right;
}
}else if(cur->_right == nullptr) {
if(parent == nullptr) _root = cur->_left;
else {
if(cur == parent->_left) parent->_left = cur->_left;
else parent->_right = cur->_left;
}
}else {
Node* minright = cur->_right;
while(minright->_left != nullptr) {
minright = minright->_left;
}
minright->_left = cur->_left;
if(parent == nullptr)
_root = cur->_right;
else {
if(cur == parent->_left) {
parent->_left = cur->_right;
}else parent->_right = cur->_right;
}
}
delete cur;
return true;
}
}
return false;
}
template<class T,class V,class Compare>
typename BSTree<T,V,Compare>::Node* BSTree<T,V,Compare>::Find(const T& src) {
Node* cur = _root;;
if(cur == nullptr) return nullptr;//如果什么都没有肯定就是直接返回的是NULL
while(cur) {
if(cmp(src,cur->_val)) cur = cur->_left;
else if(cmp(cur->_val,src)) cur = cur->_right;
else return cur;
}
return nullptr;
}
}
上面是的我的BSTree的模版的实现,如果有什么问题欢迎指正
简述搜索二叉树
二叉树大家应该都有所了解,就是在链表的基础上存储了两个节点,当然后续的多叉树的理论亦是如此,那么什么是搜索二叉树呢?就是对于每个节点来说他的左孩子节点一定小于(或者大于)他自己,右孩子节点相反,这种结构有什么优势吗?
比如说这里你的存储结构是一个vector,如果你想要找到一个值或者插入一个值,每次你都需要遍历他的几乎所有位置,list同样如此,那如果是搜索二叉树呢?每次我都可以判断我和当前的节点的大小的相对关系来判断是向左还是向右,每次判断我都能舍弃当前位置下的一半的节点数,因此,理想的状态下,搜索二叉树的时间复杂度是logn
而vector和list是o (n)
二叉树的基本框架,函数的架构
基本的我们和vector和list一样只需要存储root节点即可,以及用size来记录二叉树的节点的个数
- push()
- pop()
- find()
- iterator
- size()
- clear()
基本的底层逻辑实现这些即可,多余的后续的map和set中会提及
二叉树中的push()
很多人想到二叉树的第一个思路肯定是递归,这种思路完全没问题,但是只是有些时候没有必要,多利用栈的结构或者其他的结构多元的处理这一类的问题有助于你代码的提升
其实这里的push的原理很简单,我们的目的就是找到一个适合这个新节点的nullptr的位置,然后在这个位置链上原来的树
bool BSTree<T,V,Compare>::push(const T& val,const V& va){
::mycode::PosfromBSTree position = ::mycode::PosfromBSTree::left;
Node* parent = nullptr;
Node* cur = _root;
if(cur == nullptr) {
_root = new Node(val);
return true;
}
while(cur) {
if(cmp(val,cur->_val)) {
parent = cur;
cur = cur->_left;
position = ::mycode::PosfromBSTree::left;
}else if(cmp(cur->_val,val)) {
parent = cur;
cur = cur->_right;
position = ::mycode::PosfromBSTree::right;
}
else return false;
}
cur = new Node(val,va);
if(position == ::mycode::PosfromBSTree::left) parent->_left = cur;
else parent->_right = cur;
_size++;
return true;
}
不用着急,这里的position只是想确定我们这里的cur是parent的左孩子还是右孩子节点,当然,你也可以用if(cur == parent->_left)之类的来判断,这里我用枚举只是为了锻炼一下用enum的能力,我们通过cur找到适合的位置,然后将cur链到parent上一切就大功告成了,值得注意的唯一的一个点就是if(root = nullptr)的时候,这个时候我们需要单独考虑,当然,熟悉数据结构的coder肯定会很熟练的判断root 是否为nullptr
搜索二叉树的pop()
搜索二叉树作为其最复杂的函数,这里我提供两种思路
首先我们需要判断我们删除的节点是否存在,比如说你想删的数据压根不在,那肯定谈不上删除,其次,删除的节点的孩子个数使我们需要关注的
1.当孩子的个数是一个或者没有的时候,这个时候我们只需将其中的孩子节点,链到父亲节点的上面
2.当孩子节点的个数是两个的时候,这个时候的处理方式是整个搜索二叉树最困难的部分,这里我们有两种解决的思路
- 第一种就是找到找到右树中最小的节点来替换我们想要删除的位置
- 由于我们想要删除的节点有两个孩子节点,我们将左孩子链到右孩子树的最小节点的左边将右孩子树的根节点链到我们删除的位置
我下面的代码的逻辑就是采用了第二种的原理,第二种对于新手来说更容易操作,第一种可以读者自己去尝试一下
bool BSTree<T,V,Compare>::pop(const T& val){
Node* parent = nullptr;
Node* cur = _root;
if(cur == nullptr) return false;
while(cur) {
if(cmp(val,cur->_val)) {
parent = cur;
cur = cur->_left;
}
else if(cmp(cur->_val,val)) {
parent = cur;
cur = cur->_right;
}
else {
if(cur == nullptr) return false;//没找到直接返回false
//找到节点
//0个或者一个孩子的情况可以统一考虑
if(cur->_left == nullptr) {
if(parent == nullptr) _root = cur->_right;
else {
if(cur == parent->_left) parent->_left = cur->_right;
else parent->_right = cur->_right;
}
}else if(cur->_right == nullptr) {
if(parent == nullptr) _root = cur->_left;
else {
if(cur == parent->_left) parent->_left = cur->_left;
else parent->_right = cur->_left;
}
}else {
Node* minright = cur->_right;
while(minright->_left != nullptr) {
minright = minright->_left;
}
minright->_left = cur->_left;
if(parent == nullptr)
_root = cur->_right;
else {
if(cur == parent->_left) {
parent->_left = cur->_right;
}else parent->_right = cur->_right;
}
}
delete cur;
return true;
}
}
return false;
}
、、、、、、、、、、、、、、、、
总结
由于我的博客是一系列的,后续我还会陈述搜索二叉树的问题,并且会提出AVL树,红黑树等更高阶的数据结构,当然,他们都属于搜索二叉树,还有stl标准库中的map和set的运用,由于map和set的底层是红黑树,因此,预知后续如何,请看后续