二叉查找树(Binary Search Tree)(二叉搜索树,二叉排序树)它或者是一棵空树,或者是具有下列性质的二叉树:
1. 每个节点都有一个作为搜索依据的关键码(key),所有节点的关键码互不同。
2. 左子树上所有节点的关键码(key)都小于根节点的关键码(key)。
3. 右子树上所有节点的关键码(key)都大于根节点的关键码(key)。
4. 左右子树都是二叉搜索树。
对于二叉搜索树,在实现它的结点的插入、删除、查找时,插入结点不是很复杂,但是对于其删除有一定的难度。在实现删除时,要考虑的情况较多。
一、对二叉树的定义:
template<class K,class V>
struct BSTNode
{
K _key;
V _value;
BSTNode<K, V>* _Left;
BSTNode<K, V>* _Right;
BSTNode(K key,V value)
:_key(key)
,_value(value)
,_Left(NULL)
,_Right(NULL)
{}
};
template<class K, class V>
class BSTree
{
typedef BSTNode<K, V> Node;
public:
BSTree()
:_root(NULL)
{}
}
对于定义,应该没什么问题。那就来说说它的插入、删除、查找的实现。
二、插入的实现
对于插入,在此我用两种方法来实现(递归和非递归)
首先,非递归的方法:(在插入时,都要按照二叉树的性质来实现即要在合适位置)
思想:1、先考虑根结点为空。
2、保存上一个节点,插入时要找上一个结点。
3、找到合适的位置(要插入的key小于当前key就往左子树找反之往右子树找)
4、找到合适位置后,插入结点。
bool Insert(const K& key, const V& value)
{
if (_root == NULL)
{
_root = new Node(key, value);
return true;
}
Node* cur = _root;
Node* parent = NULL;
while (cur)
{
if (cur->_key > key)
{
parent = cur;
cur = cur->_Left;
}
else if (cur->_key < key)
{
parent = cur;
cur = cur->_Right;
}
else
{
return false;
}
}
if (parent->_key > key)
{
parent->_Left = new Node(key, value);
}
else
{
parent->_Right = new Node(key, value);
}
return true;
}
非递归完成后,我们来看一下Insert的递归的形式
bool _Insert_R(Node* &root, const K& key, const V& value)
{
if (root == NULL)
{
root = new Node(key, value);
return true;
}
if (root->_key > key)
{
return _Insert_R(root->_Left,key,value);
}
else if (root->_key < key)
{
return _Insert_R(root->_Right,key,value);
}
return false;
}
接下来就是的查找和删除,先看一下查找:
三、查找的实现(非递归形式):
Node* _Find(Node* root, 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;
}
查找的递归形式:
Node* _Find_R(Node* &root, const K& key)
{
if (root == NULL) //没有结点
return root;
if (root->_key == key)
return root;
if (root->_key < key)
{
return _Find_R(root->_Right, key);
}
else
{
return _Find_R(root->_Left, key);
}
}
四、删除的实现:(非递归)对于删除,在此需要考虑的情况非常多。
下面是删除的思想:
(在删除时,也要和查找一样,先查找到要删除的结点,然后删除)
1、考虑根结点为空情况和一个结点情况
2、当有多个结点时,根据上图中的情形考虑到不同情况
(1)要删除的左结点的左子树或右子数为空。
(2)要删除的结点是根结点。
(3)要删除的结点的左右结点都不为空。
bool _Remove(Node* root, const K& key)
{
if (root == NULL) //没有结点
return false;
if (root->_Left == NULL&&root->_Right == NULL) //一个结点
{
if (root->_key == key)
{
delete root;
root = NULL;
return true;
}
else
{
return false;
}
}
Node* cur = root; //多个节点
Node* prev = NULL;
while (cur)
{
if (cur->_key > key)
{
prev = cur;
cur = cur->_Left;
}
else if (cur->_key < key)
{
prev = cur;
cur = cur->_Right;
}
else //找到了要删除的结点,判断左子树是否为空,若为空,把删除点的左子树设为删除点右子树的左子树
{
Node *del = cur;
if (cur->_Left == NULL)
{
if (prev== NULL)
{
_root = cur->_Right;
}
else
{
if (prev->_Left == cur)
{
prev->_Left = cur->_Right;
}
else
{
prev->_Right = cur->_Right;
}
}
}
else if (cur->_Right == NULL)
{
if (prev == NULL){
_root= cur->_Left;
}
else
{
if (prev->_Left == cur)
{
prev->_Left = cur->_Left;
}
else
{
prev->_Right = cur->_Left;
}
}
}
else //左右都不为空
{
prev = cur;
Node* fistleft = cur->_Right;
while (fistleft->_Left)
{
prev = fistleft;
fistleft = fistleft->_Left;
}
swap(cur->_key, fistleft->_key);
swap(cur->_value, fistleft->_value);
del = fistleft;
if (prev->_Left == fistleft)
{
prev->_Left = fistleft->_Right;
}
else
{
prev->_Right = fistleft->_Right;
}
}
delete del;
return true;
}
}
return false;
}
删除的递归形式:
bool _Remove_R(Node* &root, const K& key) //引用
{
if (root == NULL)
{
return false;
}
if (root->_key < key)
{
return _Remove_R(root->_Right,key);
}
else if (root->_key > key)
{
return _Remove_R(root->_Left,key);
}
else
{
if (root->_Left == NULL)
{
root = root->_Right;
}
else if (root->_Right==NULL)
{
root = root->_Left;
}
else
{
Node* firstleft = root->_Right;
while (firstleft->_Left)
{
firstleft = firstleft->_Left;
}
swap(firstleft->_key, root->_key);
swap(firstleft->_value, root->_value);
_Remove_R(firstleft,key);
}
return true;
}
}
五、二叉查找树的时间复杂度
它和二分查找一样,插入和查找的时间复杂度均为lgN,但是在最坏的情况下仍然会有N的时间复杂度。原因在于插入和删除元素的时候,树没有保持平衡。