概念:二叉查找树(Binary Search Tree) (又:二叉搜索树,二叉排序树)
它或者是一棵空树,或者是具有下列性质的二叉树:
若它的左子树不空,则左子树上所有结点的值均小于它的根结点的值;
若它的右子树不空,则右子树上所有结点的值均大于它的根结点的值;
它的左、右子树也分别为二叉排序树。
当我们对二叉搜索树进行中序遍历时,可以得到一个关键字的有序序列。
插入:
在向二叉搜索树中插入节点时
首先需要判断树是否存在。
如若是空树,则直接插入。
如果存在,则对插入的位置进行查找,在这里我们不允许相同的键值存在。
找到插入位置后,还应判断是在根节点的左子树还是右子树。
节点类型:
template<class K>
struct BinarySearchTreeNode // 二叉搜索树的节点
{
BinarySearchTreeNode<K> *_left;
BinarySearchTreeNode<K> *_right;
K _key;
BinarySearchTreeNode(const K& key)
:_key(key)
, _left(NULL)
, _right(NULL)
{}
};
实现
template<class K>
class BinarySearchTree
{
public:
typedef BinarySearchTreeNode<K> Node;
BinarySearchTree()
:_root(NULL)
{}
);
bool Insert(const K& key) { //插入
// 1. 空树
// Node*cur 当前节点 Node*parent 上一次寻找后的节点
// 2.不是空树,则找到一个空的位置
// 如果 key大于cur->_key 则寻找右树,否则寻找左树
// 如果此值已经存在,则返回一个false 因为不支持数据冗余
// 找到最后一层根节点后插入
if (_root == NULL){
_root = new Node(key);
return true;
}
Node *parent = NULL;
Node *cur = _root;
while (cur){
if (cur->_key < key) {
parent = cur;
cur = cur->_right;
}
else if (cur->_key > key) {
parent = cur;
cur = cur->_left;
}
else //数据相同,冗余
return false;
}
if (key < parent->_key)
parent->_left = new Node(key);
else
parent->_right = new Node(key);
return true;
}
删除:
在对搜索二叉树进行删除的时候,我们需要考虑以下情况:
1. 二叉树是否存在。
2. 要删除的节点是页节点。
3. 要删除的节点的左子树存在。
4. 要删除的节点的右子树存在。
5. 要删除的节点的左右子树都存在。
6. 要删除的节点不存在。
第一种和第六种情况可以直接返回。
第二种情况直接对节点进行删除。
对于第三种情况:
我们将它的左子树与该节点的父节点的左/右(该节点所在位置)相连,然后删除该节点即可。
对于第四中情况的处理方式与第三中相同。
对于第五种情况我们不可以直接对该点进行删除
我们可以将该点与其左子树中的最大值进行交换(左子树的最右树),然后就像删除页节点一样可以直接进行删除。
在以上的删除情况中我们都要对如果删除的节点是根节点进行判断,否则程序会出现崩溃情况.
实现:
bool Remove(const K& key) { //删除
if (_root == NULL)
return false;
// 删除后应该将其父亲的该节点置空
// cur parent
// 替换删除 该节点左树中最大的/右数中最小的
Node *parent = NULL;
Node *cur = _root;
while (cur) {
if (cur->_key < key){
parent = cur;
cur = cur->_right;
}
else if (cur->_key > key) {
parent = cur;
cur = cur->_left;
}
else{ //找到了
// 1. 左为空
// 2. 左不为空 右为空
// 3. 左右都不为空
Node *del = cur;
if (cur->_left == NULL) {
if (parent == NULL)
_root = cur->_right;
else {
if (cur == parent->_right)
parent->_right = cur->_right;
else
parent->_left = cur->_right;
}
}
else if (cur->_right == NULL){
if (parent == NULL)
_root = cur->_left;
else {
if (cur == parent->_right)
parent->_right = cur->_left;
else
parent->_left = cur->_left;
}
}
else{ //找左树的最右节点或者 **右树的最左节点
Node *parent = cur;
Node *subLeft = cur->_right;
while (subLeft->_left) {
parent = subLeft;
subLeft = subLeft->_left;
}
cur->_key = subLeft->_key;
del = subLeft;
// 不可以直接让parent 的左 指向 cur 的右
if (parent->_left == subLeft)
parent->_left = subLeft->_right;
else
parent->_right = subLeft->_left;
}
delete del;
return true;
}
}
return false;
}
查找:
根据左树比根节点小,右树比根节点大的特点进行查找
实现:
Node * Find(const K& key) { //查找
if (_root == NULL)
return NULL;
Node* cur = _root;
while (cur)
{
if (cur->_key > key)
cur - cur->_left;
else if (cur->_key < key)
cur = cur->_right;
else
return cur;
}
return NULL;
}
由这张图我们可以发现,查询的时间复杂度大致为 O(log(N))
但是,当我们所获得的搜索二叉树是一个单支树时,查询的时间复杂度则会变为 O(N)
所以搜索二叉树的时间复杂度大致在 O(log(N))~O(N)之间,这种情况将在平衡搜索二叉树中解决。