优点:
可以将链表插入的灵活性和有序数组查找的高效性结合起来。
C++实现
template <class T>
class BSTree {
private:
BSTNode<T> *mRoot; // 根结点
public:
BSTree();
~BSTree();
// 前序遍历"二叉树"
void preOrder();
// 中序遍历"二叉树"
void inOrder();
// 后序遍历"二叉树"
void postOrder();
// (递归实现)查找"二叉树"中键值为key的节点
BSTNode<T>* search(T key);
// (非递归实现)查找"二叉树"中键值为key的节点
BSTNode<T>* iterativeSearch(T key);
// 查找最小结点:返回最小结点的键值。
T minimum();
// 查找最大结点:返回最大结点的键值。
T maximum();
// 找结点(x)的后继结点。即,查找"二叉树中数据值大于该结点"的"最小结点"。
BSTNode<T>* successor(BSTNode<T> *x);
// 找结点(x)的前驱结点。即,查找"二叉树中数据值小于该结点"的"最大结点"。
BSTNode<T>* predecessor(BSTNode<T> *x);
// 将结点(key为节点键值)插入到二叉树中
void insert(T key);
// 删除结点(key为节点键值)
void remove(T key);
// 销毁二叉树
void destroy();
// 打印二叉树
void print();
private:
// 前序遍历"二叉树"
void preOrder(BSTNode<T>* tree) const;
// 中序遍历"二叉树"
void inOrder(BSTNode<T>* tree) const;
// 后序遍历"二叉树"
void postOrder(BSTNode<T>* tree) const;
// (递归实现)查找"二叉树x"中键值为key的节点
BSTNode<T>* search(BSTNode<T>* x, T key) const;
// (非递归实现)查找"二叉树x"中键值为key的节点
BSTNode<T>* iterativeSearch(BSTNode<T>* x, T key) const;
// 查找最小结点:返回tree为根结点的二叉树的最小结点。
BSTNode<T>* minimum(BSTNode<T>* tree);
// 查找最大结点:返回tree为根结点的二叉树的最大结点。
BSTNode<T>* maximum(BSTNode<T>* tree);
// 将结点(z)插入到二叉树(tree)中
void insert(BSTNode<T>* &tree, BSTNode<T>* z);
// 删除二叉树(tree)中的结点(z),并返回被删除的结点
BSTNode<T>* remove(BSTNode<T>* &tree, BSTNode<T> *z);
// 销毁二叉树
void destroy(BSTNode<T>* &tree);
// 打印二叉树
void print(BSTNode<T>* tree, T key, int direction);
};
最大值:
最大值在最右边
template <class T>
BSTNode<T>* BSTree<T>::maximum(BSTNode<T>* tree)
{
if (tree == NULL)
return NULL;
while(tree->right != NULL)
tree = tree->right;
return tree;
}
template <class T>
T BSTree<T>::maximum()
{
BSTNode<T> *p = maximum(mRoot);
if (p != NULL)
return p->key;
return (T)NULL;
}
最小值:
最小值在最左边
template <class T>
BSTNode<T>* BSTree<T>::minimum(BSTNode<T>* tree)
{
if (tree == NULL)
return NULL;
while(tree->left != NULL)
tree = tree->left;
return tree;
}
template <class T>
T BSTree<T>::minimum()
{
BSTNode<T> *p = minimum(mRoot);
if (p != NULL)
return p->key;
return (T)NULL;
}
结点的前驱:
- 存在左孩子,那么其左孩子的最大值就是,这个的前驱,如2的前驱是1
- 如果没有左孩子。可能有两种可能
(1)x是一个右孩子,那么x的前驱使其父节点,如6的前驱是5
(2)x是一个左孩子,分两种情况
a. -1的前驱为空
b. 4的前驱为2
/*
* 找结点(x)的前驱结点。即,查找"二叉树中数据值小于该结点"的"最大结点"。
*/
template <class T>
BSTNode<T>* BSTree<T>::predecessor(BSTNode<T> *x)
{
// 如果x存在左孩子,则"x的前驱结点"为 "以其左孩子为根的子树的最大结点"。
if (x->left != NULL)
return maximum(x->left);
// 如果x没有左孩子。则x有以下两种可能:
// (01) x是"一个右孩子",则"x的前驱结点"为 "它的父结点"。
// (01) x是"一个左孩子",则查找"x的最低的父结点,并且该父结点要具有右孩子",找到的这个"最低的父结点"就是"x的前驱结点"。
BSTNode<T>* y = x->parent;
while ((y!=NULL) && (x==y->left)) //此处如果是结点-1的情况,最终的y就会变成NULL,因为根结点父节点为空
{
x = y;
y = y->parent;
}
return y;
}
结点的后继
- 如果x存在右孩子,则在右孩子的子树中查找最小结点
- 如果没有右孩子,分两种情况
(1)x是一个左孩子,则x的后继结点为它的父节点,如-1的后继为0,4的后继为5
(2)x是一个右孩子,分两种情况
a.1的后继是2
b. 6的后继为空
/*
* 找结点(x)的后继结点。即,查找"二叉树中数据值大于该结点"的"最小结点"。
*/
template <class T>
BSTNode<T>* BSTree<T>::successor(BSTNode<T> *x)
{
// 如果x存在右孩子,则"x的后继结点"为 "以其右孩子为根的子树的最小结点"。
if (x->right != NULL)
return minimum(x->right);
// 如果x没有右孩子。则x有以下两种可能:
// (01) x是"一个左孩子",则"x的后继结点"为 "它的父结点"。
// (02) x是"一个右孩子",则查找"x的最低的父结点,并且该父结点要具有左孩子",找到的这个"最低的父结点"就是"x的后继结点"。
BSTNode<T>* y = x->parent;
while ((y!=NULL) && (x==y->right)) //为1的时候,可以找到结点2,为6的时候,由于y一直向上找,最后成为了NULL
{
x = y;
y = y->parent;
}
return y;
}
插入结点
从根开始遍历,比结点小就查左子树,比结点大就查右子树,如果查的结点为空,就将其链接在该位置
/*
* 将结点插入到二叉树中
*
* 参数说明:
* tree 二叉树的根结点
* z 插入的结点
*/
template <class T>
void BSTree<T>::insert(BSTNode<T>* &tree, BSTNode<T>* z)
{
BSTNode<T> *y = NULL;
BSTNode<T> *x = tree;
// 查找z的插入位置
while (x != NULL)
{
y = x; // y用来记录最终找到的位置,该二叉查找树,不会自己调整顺序,故直接作为叶子结点插入
if (z->key < x->key)
x = x->left;
else (z->key > x->key)
x = x->right;
else //此处用来限制是否能插入相同的值
{
free(z);
return;
}
}
z->parent = y;
if (y==NULL)
tree = z;
else if (z->key < y->key)
y->left = z;
else
y->right = z;
}
/*
* 将结点(key为节点键值)插入到二叉树中
*
* 参数说明:
* tree 二叉树的根结点
* key 插入结点的键值
*/
template <class T>
void BSTree<T>::insert(T key)
{
BSTNode<T> *z=NULL;
// 如果新建结点失败,则返回。
if ((z=new BSTNode<T>(key,NULL,NULL,NULL)) == NULL)
return ;
insert(mRoot, z);
}
删除结点
- 删除结点有三种情况:
(1) 删除-1,直接删除即可
(2) 删除1,直接删除即可
(3) 删除0,找到0的后继结点,给0结点赋1的值,删掉1这个结点,这样就避免了对树做大的变动,删除的都是叶子结点了
/*
* 删除结点(z),并返回被删除的结点
*
* 参数说明:
* tree 二叉树的根结点
* z 删除的结点
*/
template <class T>
BSTNode<T>* BSTree<T>::remove(BSTNode<T>* &tree, BSTNode<T> *z)
{
BSTNode<T> *x=NULL;
BSTNode<T> *y=NULL;
if ((z->left == NULL) || (z->right == NULL) ) //z的度为2的时候,对该结点进行操作
y = z;
else
y = successor(z); //如果z的度不为2,找其后继结点操作
if (y->left != NULL) //操作y结点的子节点,此时的y结点要么是蛋羹
x = y->left;
else
x = y->right;
if (x != NULL)
x->parent = y->parent; //此处把x结点关联了y结点的父节点,但y结点的父节点的子树还没有变
if (y->parent == NULL) //如果删除的是根结点,且根结点没有右子树,执行这一步
tree = x;
else if (y == y->parent->left) //把x放到y原来的位置上
y->parent->left = x;
else
y->parent->right = x;
//由于删除度为2的结点之后,树的结构发生了变化,用后继结点代替了源结点的位置,
//所以真正删了的是后继结点,此时只需要把改变结点的值就行了,不用真正去改动结构
if (y != z)
z->key = y->key;
return y;
}
/*
* 删除结点(z),并返回被删除的结点
*
* 参数说明:
* tree 二叉树的根结点
* z 删除的结点
*/
template <class T>
void BSTree<T>::remove(T key)
{
BSTNode<T> *z, *node;
if ((z = search(mRoot, key)) != NULL)
if ( (node = remove(mRoot, z)) != NULL)
delete node;
}
打印二叉树
/*
* 打印"二叉查找树"
*
* key -- 节点的键值
* direction -- 0,表示该节点是根节点;
* -1,表示该节点是它的父结点的左孩子;
* 1,表示该节点是它的父结点的右孩子。
*/
template <class T>
void BSTree<T>::print(BSTNode<T>* tree, T key, int direction)
{
if(tree != NULL) //先序遍历
{
if(direction==0) // tree是根节点
cout << setw(2) << tree->key << " is root" << endl;
else // tree是分支节点
cout << setw(2) << tree->key << " is " << setw(2) << key << "'s " << setw(12) << (direction==1?"right child" : "left child") << endl;
print(tree->left, tree->key, -1);
print(tree->right,tree->key, 1);
}
}
template <class T>
void BSTree<T>::print()
{
if (mRoot != NULL)
print(mRoot, mRoot->key, 0);
}
销毁二叉树
/*
* 销毁二叉树
*/
template <class T>
void BSTree<T>::destroy(BSTNode<T>* &tree) //后续遍历删除
{
if (tree==NULL)
return ;
if (tree->left != NULL)
return destroy(tree->left);
if (tree->right != NULL)
return destroy(tree->right);
delete tree;
tree=NULL;
}
template <class T>
void BSTree<T>::destroy()
{
destroy(mRoot);
}