一、简单介绍:本篇博客主要包含的内容
1、二叉查找树的概念,性质
2、二叉查找树的基本操作
(1)、节点结构介绍
(2)、构造/拷贝构造/运算符重载/树拷贝实现
(3)、插入操作
(4)、查找key的值
(6)、查找key的节点或向下/向上取整的节点
(7)、查找某个key的排名
(8)、获取排名为k的节点数据
(9)、二叉树删除操作
说明:以上操作使用C++的模板结构,主要以递归方式实现,也可以借助栈实现迭代版本的。
3、二叉查找树的完整代码链接
进入主体开始!!
二、二叉树的概念,性质。
1、二叉树是一个高效的查找树,查找效率很高。
2、二叉树键值key值关系:左节点 < 根节点 < 右节点。
3、二叉树中序遍历是一个有序的序列。
4、二叉查找树的插入/查找都以键值key进行比较,选择走那条路径。
5、二叉查找树的时间复杂度:O(N),平局时间复杂度:O(lgN)
6、当数据有序时,二叉树退化成单链表,效率变低。解决方法:使用平衡树或红黑树。
三、二叉树操作实现
1、节点数据结构:
template<class T,class V>
struct BSTNode{
T _key;
V _value;
size_t _count; //节点作为根节点时对应的节点数目
BSTNode<T,V> *_left;
BSTNode<T,V> *_right;
BSTNode(T key,V value)
:_key(key)
,_value(value)
,_count(1)
,_left(NULL)
,_right(NULL)
{}
};
说明:_count用于统计当前节点作为根节点时,节点个数。用于查找某个key的排名或获取某排名的key节点。,若不进行此操作,可不用此项,防止统计出错。
2、节点的构造/拷贝构造/运算符重载/析构
template<class T,class V>
class BSTree{
public:
typedef struct BSTNode<T,V> BSTNode;
BSTree() //构造函数
:_root(NULL)
{}
BSTree(const BSTree &bst) //拷贝构造
:_root(NULL)
{
_root = _copy(_root,bst._root); //深拷贝,进行节点复制
}
BSTree &operator=(const BSTree &bst) { //运算符重载
if (this != &bst) {
BSTree<T,V> tmp(bst);
swap(this->_root, tmp._root); //注:此处交换的是root的地址
}
return *this;
}
//后序遍历删除
~BSTree() {
Delete(_root);
_root = NULL;
}
//树拷贝---前序遍历进行拷贝(递归实现)
1、根节点为空,则退出。
2、为节点开辟空间,并拷贝节点的数据。
3、左/右不为空,节点的左/右进行=递归拷贝左或右子树
4、返回节点的地址。
BSTNode* _copy(BSTNode *root, const BSTNode *bst) {
if (bst == NULL) return NULL;
root = new BSTNode(bst->_key,bst->_value);
root->_count = bst->_count;
if (bst->_left ) {
(root)->_left = _copy(root, bst->_left);
}
if (bst->_right) {
(root)->_right = _copy(root, bst->_right);
}
return root; //返回拷贝后树的根
}
private:
BSTNode *_root;
};
3、节点插入操作—递归实现
1、根为空,则创建一个新的节点,并返回。(注:根节点需接收返回值)
2、如果key < 根的key,根的左=递归根的左
3、如果key > 根的key,根的右=递归根的右
4、否则(相等)根的值(value) = value。//注:此处是替换原有key的value
5、计算根的节点个数。节点个数=左子树个数+右子树个数+1(根节点)
6、返回根节点。
void insert(T key, V value) {
_root = _insert(_root, key, value);
}
BSTNode* _insert(BSTNode* root,T key, V value) {
if (root == NULL) {
return new BSTNode(key, value);
}
if (root->_key > key) {
root->_left = _insert(root->_left, key, value);
}
else if (root->_key < key) {
root->_right = _insert(root->_right, key, value);
}
else {
root->_value = value;
}
root->_count = Size(root->_left) + Size(root->_right) + 1;
return root;
}
4、查找键值为key的值value
1、获取根节点
2、循环节不为空
3、节点key==key,则返回节点的value
4、节点key > key, 节点向左遍历.
5、节点key < key, 节点向右遍历
6、退出循环则为找到,返回一个标记 如:-1
//查找
V Find(T key) {
BSTNode *cur = _root;
while (cur) {
if (cur->_key == key) {
return cur->_value;
}else if (cur->_key > key) {
cur = cur->_left;
} else {
cur = cur->_right;
}
}
return -1; //未找到返回-1
}
7、查找key向上/向下取整key的节点
向上取整:取大于key,但最接近key的值。
向下取整:取小于key,但最接近key的值。
1、查找向上取整的数—递归实现
节点为空,则返回NULL,表示未找到数据。
如果节点的key==key,表示找到向等值,返回该节点。
如果节点的key < key,表示,向上取整的数在右子树,return 递归右子树
节点key > key,获取并递归左子树。
如果不为空,则该节点为最接近key的节点,返回退出。为找到,则当前根节点为最接近key的节点,返回该节点退出。
V findNearUp(T key) {
BSTNode *tmp = find_near_up(_root, key);
if (tmp) return tmp->_key;
else return -1; //未找到返回-1
}
BSTNode* find_near_up(BSTNode* root, T key) {
if (root == NULL) {
return NULL;
}
if (root->_key == key) {
return root;
}
if (root->_key < key) { //root->key小于key值,则比key大值在右子树。
return find_near_up(root->_right, key);
} //右子树未找到
BSTNode *cur = find_near_up(root->_left, key); //root->key 大与key,或右子树未找到,则在左子树找最接近key的值
if (cur != NULL) return cur; //找到相等的值,则返回该节点
else return root; //未找到,则根节点为最接近key的值
}
2、向下取整
与向上取整类似,修改比较的符号,和递归的方向即可。
V findNearDown(T key) {
BSTNode* tmp = find_near_down(_root, key);
if (tmp) return tmp->_key;
else return -1;
}
BSTNode* find_near_down(BSTNode *root, T key) {
if (root == NULL) return NULL;
if (root->_key == key) {
return root;
}
if (root->_key > key) { //root->key > key,则最接近小于key的值在左子树
return find_near_down(root->_left, key);
}
BSTNode *cur = find_near_down(root->_right, key); //root->key < key,最接近key的值在右子树
if (cur) return cur; //找到最接近的值
else return root; //根为最接近的值
}
8、查找第k大小的节点
节点作为根节点时,该节点的排名即该节点左子树的节点个数(注:从0算起,因为当前节点未统计),
节点排名=节点左子树几点的个数。
右子树节点的排名=左子树节点个数+1+递归该节点的右
1、如果左子数的节点个数 > k,则该节点在左子树
2、如果左子的个数 < k ,则该节点在右子树。即查找排名为 k - 左子树节点个数-1(根节点)
V find_sizeth(int k){
return _find_sizeth(_root, k - 1); //统计节点的排名从0算起。所以-1
}
V _find_sizeth(BSTNode* root, int k) {
if (root == NULL) return 0;
int size_left = Size(root->_left);
if (size_left == k) return root->_value; //与左子树节点个数相同,即排名为k的节点
if (size_left > k) {
return _find_sizeth(root->_left, k);
}
if (size_left < k) {
return _find_sizeth(root->_right, k - size_left - 1);
}
}
9、查找某个key的排名
1、节点为空则返回标志-1,表示未找到该key
2、节点的key == key ,则返回节点左子树的节点个数。
3、节点的key > key,则返回递归在左子树查找。
4、节点的key < key,则返回递归在右子树查找,如果 返回统计结果为-1,则说明未找到key值,返回-1退出。否则找到key值返回 左子树节点个数+1(根)+右子树统计的个数。
//查找key的排名
int rank(T key) {
return _rank(_root, key)+1; //此处统计从0算起
}
int _rank(BSTNode *root,T key) {
if (root == NULL) return -1;
if (root->_key < key) { //右子树节点的排名=左子树节点个数+1(根节点)+右子树
int size_right = _rank(root->_right, key);
if (size_right < 0) { //注:小于0,则说明未找到该数据,直接退出,不再加上之前计算的值。
return size_right;
}
else {
return Size(root->_left) + 1 + size_right;
}
}
else if(root->_key > key){ //在左子树查找
return _rank(root->_left, key);
}
else { //key值相同
return Size(root->_left); //返回左子树节点个数即为排名
}
}
10、删除节点
1、删除最小key节点
向左遍历到最左的节点,将其上一个节点指向该节点不为空的节点(右节点)上。删除该节点
注:链接方向与原来的方向保持一致,同时更新size值
void deleteMin() {
BSTNode *del = NULL;
_root = _deleteMin(_root,del); //用于接收,处理删除根节点的情况
delete del; //析构删除掉的节点
}
//递归查找删除节点,并从树中移除该节点
BSTNode* _deleteMin(BSTNode *root,BSTNode *&del) { //此处要修改del指针的指向,所以使用二级指针,或引用
if (root->_left == NULL) {
del = root;
return root->_right;
}
root->_left = _deleteMin(root->_left,del);
root->_count = Size(root->_left) + Size(root->_right) + 1; //重新计算节点个数
return root; //返回根节点
}
2、删除任意一个节点
1、删除节点只有左子树/右子树之一,删除节点,并让上一个节点指向不为空的左/右子树,方法同上
2、删除左右子树都不为空的节点:
方法1:在删除节点的左子树中找最最大节点作为替换节点,与删除节点进行数据交换,删除交换节点,方法同删除一个子树节点
方法2:删除节点的右子树最小值替换删除节点,修改替换节点的左右指针域。删除替换节点。
void deleteKey(T key) {
//_deleteKey(_root, key);
_deleteKey2(_root, key);
}
//方法2:
//寻找删除节点
BSTNode* _deleteKey2(BSTNode* root, T key) {
if (root == NULL) return NULL;
//递归寻找删除节点
if (root->_key > key) {
root->_left = _deleteKey2(root->_left, key);
}
else if (root->_key < key) {
root->_right = _deleteKey2(root->_right, key);
}
else { //找到删除节点,进行删除
//只有一个子树
if (root->_left == NULL) {
return root->_right;
}
else if (root->_right == NULL) {
return root->_left;
}
//左右节点都不为空,具有两子树
else {
BSTNode *del = root;
del->_right = _deleteMin(del->_right,root); //找出最小节点,并更新删除节点的右,并摘除,注:并未析构,
root->_right = del->_right;
root->_left = del->_left;
delete del;
}
}
root->_count = Size(root->_left) + Size(root->_right) + 1; //更新节点个数
return root; //返回替换后的节点,用于接收处理根节点情况。
}
bool _deleteKey(BSTNode *&root, T key) { //注:存在删除根节点的问题,此处使用二级指针。
if (root == NULL) return false;
//递归查找删除节点
if (root->_key > key) {
_deleteKey(root->_left, key);
}
else if (root->_key < key) {
_deleteKey(root->_right, key);
}
//找到删除节点
else {
BSTNode *tmp = root;
//只有左/右节点
if (root->_left == NULL) {
root = root->_right;
delete tmp;
tmp = NULL;
}
else if(root->_right == NULL){
root = root->_left;
delete tmp;
tmp = NULL;
}
//左/右节点都存在
else {
BSTNode* right_min = _keyMin(root->_right); //查找右子树最小节点,作为替换节点
_swap(root, right_min); //交换节点的数据
_deleteKey(root->_right, right_min->_key); //删除替换节点
}
//更新删除节点处的节点个数
if (root) {
root->_count = Size(root->_left) + Size(root->_right) + 1;
}
return true;
}
//更新父节点的节点个数
if (root) {
root->_count = Size(root->_left) + Size(root->_right) + 1;
}
}
void _swap(BSTNode* left, BSTNode* right) {
swap(left->_key, right->_key); //库交换函数
swap(left->_value, right->_value);
}
11、其它操作
//获取最小key
V keyMin() {
BSTNode* tmp = _keyMin(_root);
if (tmp) return tmp->_key;
else return -1;
}
BSTNode* _keyMin(BSTNode *root) {
if (root == NULL) {
return NULL;
}
if (root->_left == NULL) {
return root;
}
else {
return _keyMin(root->_left);
}
}
//获取某节点作为根节的时节点个数
int Size(BSTNode *node) {
if (node) {
return node->_count;
}
return 0;
}
//输出某范围的数据min~max
void minToMax(T min, T max) {
_minToMax(_root, min, max);
}
void _minToMax(BSTNode *root, T min, T max) {
if (root == NULL) {
return;
}
if (root->_key > min) {
_minToMax(root->_left, min, max);
}
if (min <= root->_key && max > root->_key) {
cout << root->_key << endl;
}
if (root->_key < max) {
_minToMax(root->_right, min, max);
}
}
三、完整代码链接:
https://github.com/weienjun/GetHub/tree/master/BST
有问题欢迎大家指出!!