普通的二叉树是不能进行增删改查的,或者说没意义,需要根据二叉树的某种性质,使之具有可利用的性质,那二叉搜索树是根据两数比较,除去相等外,只有大小之分的性质
再结合二叉树的特点,演化出来的树, 顾名思义,就用来搜索的二叉树。
树的概念:
二叉树——理论篇
二叉搜索树的概念:
根节点的非空左子树,都比根节点小,根节点的非空右子树,都比根节点大。
细节:
瞧,这就是一颗二叉搜索树。
概念其实不难理解,重点在于实现。
框架
给出,再对其接口
进行讲解。//二叉树结点
template<class K>
struct BSTNode
{
BSTNode(const K& key)
{
_key = key;
_left = nullptr;
_right = nullptr;
}
K _key;
BSTNode* _left;
BSTNode* _right;
};
//说明:二叉树结点是公用的,所以我们用struct(默认成员为公有)
//搜索二叉树的基本框架
template<class K>
class BSTree
{
public:
typedef BSTNode<K> BSTNode;
//增
bool Insert(const K& key);
//查
BSTNode* find(const K& key);
public:
//打印二叉搜索树
void Print();
//删
bool Erase(const K& key);
private:
void InOrder(BSTNode* root);
BSTNode* _root = nullptr;
};
功能:找到就返回二叉树结点,没找到就返回空指针。
实现原理:根据根节点的左边比根节点小,右边比根节点大,如果要查找的值比根节点大,说明在根节点的右边,否则在左边。
在此之前,我们还要讨论一个小问题,你觉得二叉搜索树的查找的时间复杂度是多大呢?
答案:在O(logN)和O(N)之间。
结合满二叉树的最好情况,和形状类似单链表的最坏情况,仔细思考一下是不是这样?
实现代码:
//查找
BSTNode* find(const K& key)
{
BSTNode* cur = _root;
while (cur)
{
if (cur->_key > key)
{
//目标值比根节点小,到左子树进行查找
cur = cur->_left;
}
else if (cur->_key < key)
{
//目标值比根节点大,到右子树进行查找
cur = cur->_right;
}
else
{
return cur;
}
}
return nullptr;
}
BSTNode* _find(BSTNode* root, const K& key)
{
if (root == nullptr)
{
return nullptr;
}
if (root->_key > key)
{
//到左子树进行查找
return _find(root->_left, key);
}
else if (root->_key < key)
{
//到右子树进行查找
return _find(root->_right, key);
}
else
{
return root;
}
}
BSTNode* find(const K& key)
{
return _find(_root, key);
}
原理分析:
细节:
当树为空时,直接改变根节点即可。
插入时,需要先保存父节点,是对父节点的对应子节点进行插入,而不是对子节点的拷贝进行插入。
非递归
非递归博主给出两种写法,喜欢哪种写哪一种。
1 . 第一种写法:保存父节点的位置,最后进行判断。
bool Insert(const K& key)
{
//如果根节点为空,那就在根节点插入即可。
if (_root == nullptr)
{
_root = new BSTNode(key);
}
else
{
BSTNode* cur = _root;
BSTNode* parent = nullptr;
while (cur)
{
if (cur->_key < key)
{
//先保存父节点的位置
parent = cur;
//根节点的值小于key,在右子树查找位置。
cur = cur->_right;
}
else if (cur->_key > key)
{
//先保存父节点的位置
parent = cur;
//根节点的值大于key,在左子树查找位置
cur = cur->_left;
}
else
{
return false;
}
}
//这里是根据父节点的位置,找到指定插入结点。
if (parent->_key > key)
{
//在左边进行插入
parent->_left = new BSTNode(key);
}
else
{
//在右边进行插入
parent->_right = new BSTNode(key);
}
return true;
}
2 . 第二种写法:使用二级指针,无需保存父节点。
bool Insert(const K& key)
{
//如果根节点为空,那就在根节点插入即可。
if (_root == nullptr)
{
_root = new BSTNode(key);
}
else
{
BSTNode** cur = &_root;
while(*cur)
{
if ((*cur)->_key > key)
{
cur = &((*cur)->_left);
}
else if ((*cur)->_key < key)
{
cur = &((*cur)->_right);
}
else
{
return false;
}
}
*cur = new BSTNode(key);
}
return true;
}
bool _Insert(BSTNode*& root, const K& key)
{
if (root == nullptr)
{
root = new BSTNode(key);
return true;
}
if (root->_key > key)
{
//到左子树进行查找
return _Insert(root->_left, key);
}
else if (root->_key < key)
{
//到右子树进行查找
return _Insert(root->_right, key);
}
else
{
return false;
}
}
bool Insert(const K& key)
{
return _Insert(_root, key);
}
这没啥好说的,很简单。
void InOrder(BSTNode* root)
{
if (root == nullptr)
{
return;
}
InOrder(root->_left);
cout << root->_key << " ";
InOrder(root->_right);
}
void Print()
{
InOrder(_root);
cout << endl;
}
细节:
实现原理:
为了便于理解,这里的删除情况,博主细分成了四种,下面一 一讲解。
很简单,父节点指向空即可。
说明:删除 1
采用方法——置换法,即找到结点左子树中最大的结点(leftMax),然后与结点的值进行交换,最后删除leftMax即可。(找到右子树最小的结点也可以,道理相同,就不赘述了)
说明:删除 1
leftMax的父节点的右结点指向leftMax的左结点
。leftMax的父节点的左结点指向leftMax的左结点
。情况分析完毕,代码实现就很简单了:
bool Erase(const K& key)
{
//大体上分为两种情况:
//第一种情况:所删除结点左右结点至少有一结点为空——细分有三种情况
//第二种情况: 所删除结点左右结点都不为空——细分只有一种情况
//先找结点
BSTNode* cur = _root;
BSTNode* parent = _root;
while (cur)
{
if (cur->_key > key)
{
parent = cur;
//到左子树进行查找
cur = cur->_left;
}
else if (cur->_key < key)
{
parent = cur;
//到右子树进行查找
cur = cur->_right;
}
else
{
//找到了
//分情况进行删除
//第一种情况:cur左右结点都为空
if (!(cur->_left || cur->_right))
{
if (cur == _root)
{
_root = nullptr;
}
else
{
//判断父节点的左节点是cur,还是右节点为cur。
if (parent->_left == cur)
{
//让父节点指向cur的right
parent->_left = nullptr;
}
else
{
parent->_right = nullptr;
}
}
}
//第二种情况:所删除结点左边为空
else if (cur->_left == nullptr)
{
if (cur == _root)
{
_root = _root->_right;
}
else
{
//判断父节点的左节点是cur,还是右节点为cur。
if (parent->_left == cur)
{
//让父节点指向cur的right
parent->_left = cur->_right;
}
else
{
parent->_right = cur->_right;
}
}
delete cur;
}
//第三种情况:所删除的右结点为空。
else if (cur->_right == nullptr)
{
if (cur == _root)
{
_root = _root->_left;
}
else
{
//判断父节点的左节点是cur,还是右节点是cur
if (parent->_left == cur)
{
parent->_left = cur->_left;
}
else
{
parent->_right = cur->_left;
}
}
delete cur;
}
//第四种情况:左右结点都不为空
else
{
//思路:找到左子树最大的结点,值进行交换,
//删除交换后的leftMax。
BSTNode* leftMax = cur->_left;
//找到最大节点
BSTNode* parent = cur;
while (leftMax->_right)
{
parent = leftMax;
leftMax = leftMax->_right;
}
//找到,值进行交换,不能是结点!
swap(leftMax->_key, cur->_key);
//交换完删除leftMax
if (parent == cur)
{
parent->_left = leftMax->_left;
}
else
{
parent->_right = leftMax->_left;
}
delete leftMax;
}
return true;
}
}
return false;
}
bool Erase(const K& key)
{
return _Erase(_root, key);
}
bool _Erase(BSTNode*& root, const K& key)
{
if (root == nullptr)
{
return false;
}
if (root->_key > key)
{
return _Erase(root->_left, key);
}
else if (root->_key < key)
{
return _Erase(root->_right, key);
}
else
{
BSTNode* del = root;
if (root->_left == nullptr)
{
root = root->_right;
}
else if (root->_right == nullptr)
{
root = root->_left;
}
else
{
//找到左子树最大的结点
BSTNode* leftMax = root->_left;
while (leftMax->_right)
{
leftMax = leftMax->_right;
}
//交换值
swap(leftMax->_key, root->_key);
//删除左子树的key值结点
return _Erase(root->_left, key);
}
delete del;
}
return true;
}
#include
using namespace std;
template<class K>
struct BSTNode
{
BSTNode(const K& key)
{
_key = key;
_left = nullptr;
_right = nullptr;
}
K _key;
BSTNode* _left;
BSTNode* _right;
};
template<class K>
class BSTree
{
public:
typedef BSTNode<K> BSTNode;
typedef BSTNode** BSTPtr;
typedef BSTNode* BSTPtr1;
//增
bool Insert(const K& key)
{
//如果根节点为空,那就在根节点插入即可。
if (_root == nullptr)
{
_root = new BSTNode(key);
}
else
{
//第一种写法:
//根据搜索二叉树的性质:左边都比跟结点小,右边都比根节点大,
//找到空位置进行插入即可。
BSTNode* cur = _root;
BSTNode* parent = nullptr;
while (cur)
{
if (cur->_key < key)
{
//先保存父节点的位置
parent = cur;
//根节点的值小于key,在右子树查找位置。
cur = cur->_right;
}
else if (cur->_key > key)
{
//先保存父节点的位置
parent = cur;
//根节点的值大于key,在左子树查找位置
cur = cur->_left;
}
else
{
return false;
}
}
//这里是根据父节点的位置,找到指定插入结点。
if (parent->_key > key)
{
//在左边进行插入
parent->_left = new BSTNode(key);
}
else
{
//在右边进行插入
parent->_right = new BSTNode(key);
}
//第二种写法:二级指针
//BSTNode** cur = &_root;
//while(*cur)
//{
// if ((*cur)->_key > key)
// {
// cur = &((*cur)->_left);
// }
// else if ((*cur)->_key < key)
// {
// cur = &((*cur)->_right);
// }
// else
// {
// return false;
// }
//}
//*cur = new BSTNode(key);
}
return true;
}
//查找
BSTNode* find(const K& key)
{
BSTNode* cur = _root;
while (cur)
{
if (cur->_key > key)
{
//目标值比根节点小,到左子树进行查找
cur = cur->_left;
}
else if (cur->_key < key)
{
//目标值比根节点大,到右子树进行查找
cur = cur->_right;
}
else
{
return cur;
}
}
return nullptr;
}
private:
void InOrder(BSTNode* root)
{
if (root == nullptr)
{
return;
}
InOrder(root->_left);
cout << root->_key << " ";
InOrder(root->_right);
}
public:
void Print()
{
InOrder(_root);
cout << endl;
}
//改
bool Erase(const K& key)
{
//大体上分为两种情况:
//第一种情况:所删除结点左右结点至少有一结点为空——细分有四种情况
//第二种情况: 所删除结点左右结点都不为空——细分只有一种情况
//先找结点
BSTNode* cur = _root;
BSTNode* parent = _root;
while (cur)
{
if (cur->_key > key)
{
parent = cur;
//到左子树进行查找
cur = cur->_left;
}
else if (cur->_key < key)
{
parent = cur;
//到右子树进行查找
cur = cur->_right;
}
else
{
//找到了
//分情况进行删除
//第一种情况:cur左右结点都为空
if (!(cur->_left || cur->_right))
{
if (cur == _root)
{
_root = nullptr;
}
else
{
//判断父节点的左节点是cur,还是右节点为cur。
if (parent->_left == cur)
{
//让父节点指向cur的right
parent->_left = nullptr;
}
else
{
parent->_right = nullptr;
}
}
}
//第二种情况:所删除结点左边为空
else if (cur->_left == nullptr)
{
if (cur == _root)
{
_root = _root->_right;
}
else
{
//判断父节点的左节点是cur,还是右节点为cur。
if (parent->_left == cur)
{
//让父节点指向cur的right
parent->_left = cur->_right;
}
else
{
parent->_right = cur->_right;
}
}
delete cur;
}
//第三种情况:所删除的右结点为空。
else if (cur->_right == nullptr)
{
if (cur == _root)
{
_root = _root->_left;
}
else
{
//判断父节点的左节点是cur,还是右节点是cur
if (parent->_left == cur)
{
parent->_left = cur->_left;
}
else
{
parent->_right = cur->_left;
}
}
delete cur;
}
//第四种情况:左右结点都不为空
else
{
//思路:找到左子树最大的结点,值进行交换,
//删除交换后的leftMax。
BSTNode* leftMax = cur->_left;
//找到最大节点
BSTNode* parent = cur;
while (leftMax->_right)
{
parent = leftMax;
leftMax = leftMax->_right;
}
//找到,值进行交换,不能是结点!
swap(leftMax->_key, cur->_key);
//交换完删除leftMax
if (parent == cur)
{
parent->_left = leftMax->_left;
}
else
{
parent->_right = leftMax->_left;
}
delete leftMax;
}
return true;
}
}
return false;
}
private:
BSTNode* _root = nullptr;
};
#include
using namespace std;
template<class K>
struct BSTNode
{
BSTNode(const K& key)
{
_key = key;
_left = nullptr;
_right = nullptr;
}
K _key;
BSTNode* _left;
BSTNode* _right;
};
template<class K>
class BSTree
{
public:
typedef BSTNode<K> BSTNode;
//增
bool _Insert(BSTNode*& root, const K& key)
{
if (root == nullptr)
{
root = new BSTNode(key);
return true;
}
if (root->_key > key)
{
//到左子树进行查找
return _Insert(root->_left, key);
}
else if (root->_key < key)
{
//到右子树进行查找
return _Insert(root->_right, key);
}
else
{
return false;
}
}
bool Insert(const K& key)
{
return _Insert(_root, key);
}
//查
BSTNode* _find(BSTNode* root, const K& key)
{
if (root == nullptr)
{
return nullptr;
}
if (root->_key > key)
{
//到左子树进行查找
return _find(root->_left, key);
}
else if (root->_key < key)
{
//到右子树进行查找
return _find(root->_right, key);
}
else
{
return root;
}
}
BSTNode* find(const K& key)
{
return _find(_root, key);
}
public:
//打印二叉搜索树
void Print()
{
InOrder(_root);
cout << endl;
}
bool _Erase(BSTNode*& root, const K& key)
{
if (root == nullptr)
{
return false;
}
if (root->_key > key)
{
return _Erase(root->_left, key);
}
else if (root->_key < key)
{
return _Erase(root->_right, key);
}
else
{
BSTNode* del = root;
if (root->_left == nullptr)
{
root = root->_right;
}
else if (root->_right == nullptr)
{
root = root->_left;
}
else
{
//找到左子树最大的结点
BSTNode* leftMax = root->_left;
while (leftMax->_right)
{
leftMax = leftMax->_right;
}
//交换值
swap(leftMax->_key, root->_key);
//删除左子树的key值结点
return _Erase(root->_left, key);
}
delete del;
}
return true;
}
//删
bool Erase(const K& key)
{
return _Erase(_root, key);
}
private:
void InOrder(BSTNode* root)
{
if (root == nullptr)
{
return;
}
InOrder(root->_left);
cout << root->_key << " ";
InOrder(root->_right);
}
BSTNode* _root = nullptr;
};
今天的分享就到这里了,如果觉得文章不错,点个赞鼓励一下吧!我们下篇文章再见
!