介绍
二叉查找树(Binary Search Tree),也被称作二叉搜索树。设x是二叉查找树中的任意一个结点,则x的键值大于等于它的左子树中任意一个结点的键值,小于等于它的右子树中任意一个结点的键值。
原理
1、结点的前驱和后继
结点的前驱:二叉树中键值小于该结点的最大结点。
结点的后继:二叉树中键值大于该结点的最小结点
查找前驱结点的方法:
如果x存在左孩子,则x的前驱结点为以其左孩子为根的子树的最大结点
如果x不存在左孩子:
1>x是一个右孩子,前驱结点就是它的父结点
2>x是一个左孩子,查找x的最低父结点,并且x位于该父结点的右子树中,前驱结点就是这个父结点
查找后继结点的方法
如果x存在右孩子,则x的后继结点为以其右孩子为根的子树的最小结点
如果x不存在右孩子:
1>x是一个左孩子,后继结点就是它的父结点
2>x是一个右孩子,查找x的最低父结点,并且x位于该父结点的左子树中,后继结点就是这个父结点
2、插入结点
插入操作均是在叶结点处进行的,因此,只要按照插入结点的值找到相应的叶结点就OK了,程序中x、y表示两个结点,x初始化为根结点,通过这两个变量寻找新结点 z 的插入位置,根据x结点的值与z结点的值的比较结果选择对应的路径,直到x等于NULL,此时y就是我们想寻找的叶节点,这时只需将y和z链接起来就行。
//插入键值为key的结点(内部接口)
template <class T>
void BSTree<T>::insert(Node<T>* &tree,Node<T> *z)
{
Node<T> *y=NULL;
Node<T> *x=tree;
//寻找z的插入位置
while(x)
{
y=x;
if(z->key<x->key)
x=x->l;
else
x=x->r;
}
if(!y)//树为空
tree=z;
else
{
z->p=y;//z的父结点为y
if(z->key<y->key)//z是y的左孩子
y->l=z;
else
y->r=z;//z是y的右孩子
}
}
//插入键值为key的结点(外部接口)
template<class T>
void BSTree<T>::insert(T key)
{
Node<T> *z=new Node<T>(key,NULL,NULL,NULL);
if(z) insert(root,z);
}
3、删除结点
根据键值进行删除操作,删除结点后还要维持二叉查找树的特点,真正被删除的结点其实是这一类结点:最多只有一个孩子结点,删除这类结点非常简单,只需要将它的孩子结点和它的父结点进行链接,再把自己释放掉就行。当被删除结点就是这类结点时,直接按上面的操作进行即可。当被删除结点不是这类结点时,就需要找到一个代替的点,这就是该结点的后继结点,将该结点的键值变为后继结点的键值,然后删除后继结点即可,容易知道后继结点是上面描述的那类结点,因此照着描述进行操作即可。
//删除结点(内部接口)
template<class T>
Node<T>* BSTree<T>::remove(Node<T>* &tree,Node<T> *z)
{
Node<T> *x=NULL,*y=NULL;//y是真正要被删除的结点,x是y的孩子结点
if(z->l&&z->r)//如果z的左右孩子都在,y就等于z的后继结点
y=successor(z);
else//否则,y=z
y=z;
//此时对于 y有3种情况 1.只存在左孩子 2.只存在右孩子 3.不存在孩子
if(y->l)//情况一
x=y->l;
else //情况二、三
x=y->r;
if(x) x->p=y->p;//如果y存在孩子结点,那么就将孩子结点的父结点变为y的父结点
if(!y->p)
tree=x;//如果y是根结点,就让根结点变为x
else if(y==y->p->l)
y->p->l=x;
else
y->p->r=x;
if(y!=z)
z->key=y->key;
return y;
}
//删除结点,并返回结点(外部接口)
template<class T>
void BSTree<T>::remove(T key)
{
Node<T> *z,*node;
if((z=search(root,key)))//查找到键值为key的结点
{
if((node=remove(root,z)))//删除成功
delete node;
}
}
C++实现
注意:程序使用了内部接口和外部接口,这样做主要是因为数据成员 root 为私有变量
#include<iostream>
using namespace std;
template<class T>
class Node{
public:
T key;//键值
Node *p;//父节点
Node *l;//左孩子
Node *r;//右孩子
Node(T key,Node *p,Node *l,Node *r):key(key),p(p),l(l),r(r){}
};
template<class T>
class BSTree{
private:
Node<T> *root;//根结点
public:
BSTree();
~BSTree();
void preOrder();//前序遍历
void inOrder();//中序遍历
void postOrder(); //后序遍历
Node<T>* search(T key); //查找二叉树中键值为key的结点 (递归实现)
Node<T>* iterativeSearch(T key); //查找二叉树中键值为key的结点(非递归实现)
T minimum();//查找最小结点:返回最小结点的键值
T maximum();//查找最大结点:返回最大结点的键值
//查找结点x的前驱结点,即查找二叉树中键值小于该结点的最大结点
Node<T>* predecessor(Node<T> *x);
//查找结点x的后继结点,即查找二叉树中键值大于该结点的最小结点
Node<T>* successor(Node<T> *x);
void insert(T key);//将键值为key的结点插入到二叉树中
void remove(T key);//删除键值为key的结点
void destroy();//销毁二叉树
void print();//打印二叉树
private:
void preOrder(Node<T> *tree);//前序遍历
void inOrder(Node<T> *tree);//中序遍历
void postOrder(Node<T> *tree);//后序遍历
Node<T>* search(Node<T>* x,T key);//查找键值为key的结点(递归实现)
Node<T>* iterativeSearch(Node<T>* x,T key);//查找键值为key的结点(非递归实现)
//查找最小结点:返回tree为根结点的二叉树的最小结点
Node<T>* minimum(Node<T>* tree);
//查找最大结点:返回tree为根节点的二叉树的最大结点
Node<T>* maximum(Node<T>* tree);
void insert(Node<T>* &tree,Node<T> *z);//插入结点z
Node<T>* remove(Node<T>* &tree,Node<T> *z);//删除并返回结点z
void destroy(Node<T>* &tree);//销毁二叉树
void print(Node<T>* tree,T key,int direction);//打印二叉树
};
//构造函数
template<class T>
BSTree<T>::BSTree()
{
root=NULL;
}
//析构函数
template<class T>
BSTree<T>::~BSTree()
{
destroy();
}
//前序遍历(内部接口)
template<class T>
void BSTree<T>::preOrder(Node<T>* tree)
{
if(tree)
{
cout<<tree->key<<" ";
preOrder(tree->l);
preOrder(tree->r);
}
}
//前序遍历(外部接口)
template<class T>
void BSTree<T>::preOrder()
{
preOrder(root);
}
//中序遍历(内部接口)
template<class T>
void BSTree<T>::inOrder(Node<T>* tree)
{
if(tree)
{
inOrder(tree->l);
cout<<tree->key<<" ";
inOrder(tree->r);
}
}
//中序遍历(外部接口)
template<class T>
void BSTree<T>::inOrder()
{
inOrder(root);
}
//后序遍历(内部接口)
template<class T>
void BSTree<T>::postOrder(Node<T> *tree)
{
if(tree)
{
postOrder(tree->l);
postOrder(tree->r);
cout<<tree->key<<" ";
}
}
//后序遍历(外部接口)
template<class T>
void BSTree<T>::postOrder()
{
postOrder(root);
}
//查找键值为key的结点(递归实现) 内部接口
template<class T>
Node<T>* BSTree<T>::search(Node<T>* x,T key)
{
if(!x||x->key==key)
return x;
if(key<x->key)
return search(x->l,key);
else
return search(x->r,key);
}
//查找键值为key的结点(递归实现) 外部接口
template<class T>
Node<T>* BSTree<T>::search(T key)
{
search(root,key);
}
//查找键值为key的结点(非递归实现) 内部接口
template<class T>
Node<T>* BSTree<T>::iterativeSearch(Node<T>* x,T key)
{
while(x)
{
if(key==x->key)
return x;
else if(key<x->key)
x=x->l;
else
x=x->r;
}
return x;
}
//查找键值为key的结点(非递归实现) 外部接口
template<class T>
Node<T>* BSTree<T>::iterativeSearch(T key)
{
iterativeSearch(root,key);
}
//查找最大值(内部接口)
template<class T>
Node<T>* BSTree<T>::maximum(Node<T>* tree)
{
if(!tree) return NULL;
while(tree->r) tree=tree->r;
return tree;
}
//查找最大值(外部接口)
template<class T>
T BSTree<T>::maximum()
{
Node<T>* p=maximum(root);
if(p)
return p->key;
else
return (T)NULL;
}
//查找最小值(内部接口)
template<class T>
Node<T>* BSTree<T>::minimum(Node<T>* tree)
{
if(!tree) return NULL;
while(tree->l) tree=tree->l;
return tree;
}
//查找最小值(外部接口)
template<class T>
T BSTree<T>::minimum()
{
Node<T>* p=minimum(root);
if(p)
return p->key;
else
return (T)NULL;
}
//结点的前驱:该结点的左子树的最大结点
//结点的后继:该结点的右子树的最小结点
//查找结点的前驱结点,即二叉树中键值小于该结点的最大结点
/*
如果x存在左孩子,则x的前驱结点为以其左孩子为根的子树的最大结点
如果x不存在左孩子:
1>x是一个右孩子,前驱结点就是它的父结点
2>x是一个左孩子,查找x的最低父结点,并且x位于该父结点的右子树中,前驱结点就是这个父结点
*/
template<class T>
Node<T>* BSTree<T>::predecessor(Node<T> *x)
{
if(x->l) return maximum(x->l);
Node<T>* y=x->p;
while(y&&(x==y->l))
{
x=y;
y=y->p;
}
return y;
}
//查找结点的后继结点,即二叉树中键值大于该结点的最小结点
/*
如果x存在右孩子,则x的后继结点为以其右孩子为根的子树的最小结点
如果x不存在右孩子:
1>x是一个左孩子,后继结点就是它的父结点
2>x是一个右孩子,查找x的最低父结点,并且x位于该父结点的左子树中,后继结点就是这个父结点
*/
template<class T>
Node<T>* BSTree<T>::successor(Node<T> *x)
{
if(x->r) return maximum(x->r);
Node<T> *y=x->p;
while(y&&(x==y->r))
{
x=y;
y=y->p;
}
return y;
}
//插入键值为key的结点(内部接口)
template <class T>
void BSTree<T>::insert(Node<T>* &tree,Node<T> *z)
{
Node<T> *y=NULL;
Node<T> *x=tree;
//寻找z的插入位置
while(x)
{
y=x;
if(z->key<x->key)
x=x->l;
else
x=x->r;
}
if(!y)//树为空
tree=z;
else
{
z->p=y;//z的父结点为y
if(z->key<y->key)//z是y的左孩子
y->l=z;
else
y->r=z;//z是y的右孩子
}
}
//插入键值为key的结点(外部接口)
template<class T>
void BSTree<T>::insert(T key)
{
Node<T> *z=new Node<T>(key,NULL,NULL,NULL);
if(z) insert(root,z);
}
//删除结点(内部接口)
template<class T>
Node<T>* BSTree<T>::remove(Node<T>* &tree,Node<T> *z)
{
Node<T> *x=NULL,*y=NULL;//y是真正要被删除的结点,x是y的孩子结点
if(z->l&&z->r)//如果z的左右孩子都在,y就等于z的后继结点
y=successor(z);
else//否则,y=z
y=z;
//此时对于 y有3种情况 1.只存在左孩子 2.只存在右孩子 3.不存在孩子
if(y->l)//情况一
x=y->l;
else //情况二、三
x=y->r;
if(x) x->p=y->p;//如果y存在孩子结点,那么就将孩子结点的父结点变为y的父结点
if(!y->p)
tree=x;//如果y是根结点,就让根结点变为x
else if(y==y->p->l)
y->p->l=x;
else
y->p->r=x;
if(y!=z)
z->key=y->key;
return y;
}
//删除结点,并返回结点(外部接口)
template<class T>
void BSTree<T>::remove(T key)
{
Node<T> *z,*node;
if((z=search(root,key)))//查找到键值为key的结点
{
if((node=remove(root,z)))//删除成功
delete node;
}
}
//打印(内部接口)
template<class T>
void BSTree<T>::print(Node<T> *tree,T key,int direction)
{
if(tree)
{
if(direction==0)
cout<<tree->key<<" is root"<<endl;
else
cout<<tree->key<<" is "<<key<<"'s"<<(direction==1?"right child":"left child")<<endl;
print(tree->l,tree->key,-1);
print(tree->r,tree->key,1);
}
}
//打印(外部接口)
template<class T>
void BSTree<T>::print()
{
if(root) print(root,root->key,0);
}
//销毁(内部接口)
template<class T>
void BSTree<T>::destroy(Node<T>* &tree)
{
if(tree)
{
if(tree->l) destroy(tree->l);
if(tree->r) destroy(tree->r);
delete tree;
tree=NULL;
}
}
//外部接口
template<class T>
void BSTree<T>::destroy()
{
destroy(root);
}