目录
1.二叉树的概念
2.二叉搜索树的操作
2.1二叉搜索树的结构
2.2实现节点的查找(find)
2.3实现增加节点(insert)
2.4实现删除节点(erase)
2.5析构函数
2.6二叉搜索树的完整实现
3.二叉搜索树的应用
3.1 K模型
3.2KV模型
4.二叉搜索树的性能分析
1.二叉树的概念
二叉搜索树也叫二叉排序数(因为按照中序遍历是有序的),对于非空二叉树满足以下性质
a.非空左子树的所有键值(key)都小于根节点的键值;b.非空右子树的所有健值都大于根节点的键值c.根节点的左右子树都是二叉搜索树。如图就是一颗二叉搜索树
2.二叉搜索树的操作
2.1二叉搜索树的结构
a.构建二叉搜索树需要定义一个struct BSTNode 节点信息,节点中包括val值,右子树节点地址,左子树节点地址。并完成构造函数b.构建二叉搜索树,还需要树本体,里面存储root根节点信息,并包装各种接口实现对其的增删查改管理。
template<class K>
struct BSTNode
{BSTNode(const K& key):_key(key):_left(nullptr):_right(nullptr){}K _key;BSTNode<K>* _left;BSTNode<K>* _right;
};template<class K>
class BSTree
{typedef BSTNode<K> Node;
public://接口实现
private:Node* _root=nullptr;
};
2.2实现节点的查找(find)
find实现可分为以下步骤:①比我小,那么迭代到左节点寻找,如果比我大,那么迭代到右节点寻找。②如果在cur为空前,都没找到,那么就没有此节点。
bool find(const K& key)
{Node* cur = _root;while (cur){if (key > cur->_key) cur = cur->right;else if (key < cur->_key) cur = cur->left;else return false;}return true;
}
2.3实现增加节点(insert)
insert的实现可分为以下步骤:①如果二叉树为空(头节点为空),直接将new的新节点赋值给_root即可②如果为非空树,那么先find找到位置(如果存在),用双指针法找,当前驱指针为空时,后继节就是我们要插入节点的父节点。
bool insert(const K& key)
{if (_root == nullptr){_root = new Node(key);return true;}Node* cur = _root;Node* prev = nullptr;while (cur){if (key > cur->_key){prev = cur;cur = cur->_right;}else if (key < cur->_key){prev = cur;cur = cur->_left;}else{return false;}}if (key > prev->_key){prev->_right = new Node(key);}else{prev->_left = new Node(key);}return true;
}
2.4实现删除节点(erase)
erase的实现比较复杂,需要考虑一下几点:通过find找到要删的节点(如果有),①如果要删除的节点左子树为空树,那么将其右树的节点代替其位置与父建立连接。②如果要删除的节点右树为空树,那么将其左树代替其位置与父建立连接。③如果左右都无子树,且有父节点,那么可复用上述①②。④如果左无子树,且无父节点,那么直接将_root置为其右树节点,同样如果无右子树,且无父节点,那么直接将_root置为其左子树节点,如果左右无子树,且无父节点,可复用。⑤如果左树与右数都不为空,那么用将其置换为左树的最右节点或者右树的最左节点,然后删除被置换的节点,注意要继承被删除节点的左子树或者右子树⑥注意在完成第⑤步骤时,可能左树的最右节点就是左树的跟(因为左树没有右子树),同理,可能右树的最左节点就是右树的跟(因为右树没有左子树)。
bool erase(const K& key)
{Node* cur = _root;Node* prev = nullptr;while (cur){if (key > cur->_key){prev = cur;cur = cur->_right;}else if (key < cur->_key){prev = cur;cur = cur->_left;}else //找到了要删除的erase节点,此时cur是我们要删除的节点,prev是其父节点{if (cur->_left == nullptr){if (prev == nullptr){_root = cur->_right;delete cur;}else {if (prev->_left == cur){prev->_left = cur->_right;}else if (prev->_right == cur){prev->_right = cur->_right;}delete cur;}}else if (cur->_right == nullptr){if (prev == nullptr){_root = cur->_left;delete cur;}else{if (prev->_left == cur){prev->_left = cur->_left;}else if (prev->_right == cur){prev->_right = cur->_left;}delete cur;}}else if (cur->_left != nullptr && cur->_right != nullptr){Node* right2left = cur->_right;Node* newprev = cur;while (right2left->_left){newprev = right2left;right2left = right2left->_left;}swap(cur->_key, right2left->_key);if (newprev->_left == right2left){newprev->_left = right2left->_right;} else if (newprev->_right == right2left){newprev->_right = right2left->_right;}delete right2left;}return true;}}return false;
}
2.5析构函数
二叉树的析构函数,可以利用函数递归到底层,再逐步删除节点利用后序遍历可以保证跟节点最后delete。
~BSTree() // 析构函数,利用函数递归进行析构
{destroy_bst(_root);
}
void destroy_bst(Node* root)
{if (root == nullptr) return;destroy_bst(root->_left);destroy_bst(root->_right);delete root;
}
2.6二叉搜索树的完整实现
#include<iostream>
using namespace std;template<class K>
struct BSTNode
{BSTNode(const K& key):_key(key),_left(nullptr),_right(nullptr){}K _key;BSTNode<K>* _left;BSTNode<K>* _right;
};template<class K>
class BSTree
{typedef BSTNode<K> Node;
public://接口实现~BSTree() // 析构函数,利用函数递归进行析构{destroy_bst(_root);}void destroy_bst(Node* root){if (root == nullptr) return;destroy_bst(root->_left);destroy_bst(root->_right);delete root;}bool find(const K& key){Node* cur = _root;while (cur){if (key > cur->_key) cur = cur->right;else if (key < cur->_key) cur = cur->left;else return false;}return true;}bool insert(const K& key){if (_root == nullptr){_root = new Node(key);return true;}Node* cur = _root;Node* prev = nullptr;while (cur){if (key > cur->_key){prev = cur;cur = cur->_right;}else if (key < cur->_key){prev = cur;cur = cur->_left;}else{return false;}}if (key > prev->_key){prev->_right = new Node(key);}else{prev->_left = new Node(key);}return true;}bool erase(const K& key){Node* cur = _root;Node* prev = nullptr;while (cur){if (key > cur->_key){prev = cur;cur = cur->_right;}else if (key < cur->_key){prev = cur;cur = cur->_left;}else //找到了要删除的erase节点,此时cur是我们要删除的节点,prev是其父节点{if (cur->_left == nullptr){if (prev == nullptr){_root = cur->_right;delete cur;}else {if (prev->_left == cur){prev->_left = cur->_right;}else if (prev->_right == cur){prev->_right = cur->_right;}delete cur;}}else if (cur->_right == nullptr){if (prev == nullptr){_root = cur->_left;delete cur;}else{if (prev->_left == cur){prev->_left = cur->_left;}else if (prev->_right == cur){prev->_right = cur->_left;}delete cur;}}else if (cur->_left != nullptr && cur->_right != nullptr){Node* right2left = cur->_right;Node* newprev = cur;while (right2left->_left){newprev = right2left;right2left = right2left->_left;}swap(cur->_key, right2left->_key);if (newprev->_left == right2left){newprev->_left = right2left->_right;} else if (newprev->_right == right2left){newprev->_right = right2left->_right;}delete right2left;}return true;}}return false;}void _InOrder(Node* root){if (root == nullptr) return;_InOrder(root->_left);cout << root->_key << " ";_InOrder(root->_right);}void InOrder(){_InOrder(_root);cout << endl;}
private:Node* _root=nullptr;
};
3.二叉搜索树的应用
3.1 K模型
即二叉搜索树的节点中只存储key一个关键码。代码就是我们上述实现的普通二叉树。
它应用在查找一个key在不在节点中,例如门禁系统:我们或是通过人脸识别,或是通过RFID射频识别技术获得一个关键码key,如果find找到在BSTree中,那么就识别成功,否则识别失败。他的速度非常快,O(log N),意味着13亿人,最多只需要31次就能找到具体的一个人。
3.2KV模型
即二叉搜索树的节点中除了key,还存储了val,通过key可以通过find映射到val。它的代码实现如下,与K模型非常相似。(它的find,erase和insert都是通过key实现的)
template<class K,class V>
struct BSTNode //binary search node
{BSTNode(const K& key,const V& val ):_key(key),_left(nullptr),_right(nullptr),_val(val){}BSTNode<K,V>* _left;BSTNode<K,V>* _right;K _key;V _val;};
template<class K,class V>
class BSTree
{typedef BSTNode<K,V> Node;
public:bool insert(const K& key,const V& val){if (_root == nullptr){_root = new Node(key,val);}Node* cur = _root;Node* prev = nullptr;while (cur){if (cur->_key > key){prev = cur;cur = cur->_left;}else if (cur->_key < key){prev = cur;cur = cur->_right;}else{return false;}}if (key < prev->_key)prev->_left = new Node(key,val);elseprev->_right = new Node(key,val);return true;}void _InOrder(Node* root) //中序遍历{if (root == nullptr)return;_InOrder(root->_left);cout << root->_key << "->"<<root->_val<<" ";_InOrder(root->_right);}void InOrder(){_InOrder(_root);}Node* find(const K& key){Node* cur = _root;while (cur){if (key > cur->_key)cur = cur->_right;else if (key < cur->_key)cur = cur->_left;elsereturn cur;}return nullptr;}bool erase(const K& key){Node* prev = nullptr;Node* cur = _root;while (cur){if (key > cur->_key){prev = cur;cur = cur->_right;}else if (key < cur->_key){prev = cur;cur = cur->_left;}else{if (cur->_left == nullptr ){//如果要删的节点是根节点if (cur == _root){_root = cur->_right;delete cur;}else{if (prev->_left == cur)prev->_left = cur->_right;else if (prev->_right == cur)prev->_right = cur->_right;delete cur;}}else if (cur->_right == nullptr){if (cur == _root){_root = cur->_left;delete cur;}else{if (prev->_left == cur)prev->_left = cur->_left;else if (prev->_right == cur)prev->_right = cur->_left;delete cur;cur = nullptr;}}else {//需要考虑特殊情况,当leftmax的右为空时Node* prev_leftmax = cur;Node* leftmax = cur->_left;while (leftmax->_right){prev_leftmax = leftmax;leftmax = leftmax->_right;}cur->_key = leftmax->_key;if (prev_leftmax->_left == leftmax)prev_leftmax->_left = leftmax->_left;elseprev_leftmax->_right = leftmax->_left;delete leftmax;leftmax = nullptr;}return true;}}if (cur == nullptr)return false;}
public:Node* _root = nullptr;
};
4.二叉搜索树的性能分析
值得注意的是,一组数据,通过不同顺序输入所构建的二叉搜索树不同。
如果构建的是一颗完全二叉树,或者接近完全二叉树,那么其查找的效率就很高,O(logN);
如果输入的数据是有序的,那么构建的二叉树就会退化为单支二叉树,那么效率就会降低,接近O(N)
后续我们学习了AVL树和红黑树就可以解决这个问题,实现一颗左右接近平衡的二叉树