二叉树的一个重要应用就是在查找中,假设树中每一个结点的数据域存储一定的数据信息,按照一定的规则就能处理这些信息。二叉搜索树是一种特殊的二叉树,对于任意一个结点,它的所有左子树结点项的值都小于其节点值,而它的所有右字数的结点的值都大于其结点值。在下图中,左边的数是二叉搜索树,而右边不是,因为其项值是6的结点在其左字数中存在项值为7的结点,不满足任意节点左子树的值小于其值。
和其他很多树的实现一样,二叉搜索树也是通过递归实现的,下面是二叉搜索树C++模板类的主要接口。
数据成员root为指向树根结点的指针,当数空时root为NULL,public接口调用对应的私有成员函数实现指定的功能。对于二叉搜索树的插入,从根结点开始查找,如果插入项小于当前结点的值,则继续往左走,如果插入项大于当前结点的值,则往又走,如果等于当前节点值,则什么都不做或者做相应的更新操作。如下图所示,为了插入5,从根结点6开始遍历,5比6小,则往根结点的左子树走。到大结点2时,5比2大,往右走,在结点4处,继续往又走,但是4不存在右子树,所以此处就是要插入的位置。
插入5以前和插入5以后的二叉搜索树
删除结点4前后的二叉搜索树
如果待删除结点有两个儿子,一般的删除策略是用其右子树的最小结点代替待删除结点的数据,然后递归删除那个右子树最小结点。如下图所示,要删除结点2,则找到结点2的右子树的最小结点3并将其数据赋值给结点2,然后在删除结点3。
删除结点2前后的二叉树
删除结点的代码实现:
template<typename T>
class BSTree{
public:
BSTree():root(NULL),currSize(0){};//默认构造函数
explicit BSTree(std::vector<T> &);//从一个数据系列中读入数据构造
BSTree(const BSTree &rhs):root(NULL),currSize(0){*this = rhs;}
BSTree &operator=(const BSTree &);
~BSTree();
T &max() const; //返回最大值
T &min() const; //返回最小值
bool contains(const T &)const; //判断是否包含某项
bool isEmpty() const; //判断树空
size_t size() const; //返回当前树的有效大小
void print(std::ostream &out = std::cout)const;//打印数结点信息
void clear(); //清空树
void insert(const T &);//插入一个结点
void remove(const T &);//删除一个结点
private:
struct Node{ //二叉搜索树的结点结构
T data; //数据域
Node *lchild; //左孩子
Node *rchild; //右孩子
Node(const T &val,Node *left =NULL,Node *right =NULL):data(val),lchild(left),rchild(right){}
};
Node *root;
size_t currSize;
void insert(Node **tree,const T &val);//在树tree中插入一个结点
void remove(Node **tree,const T &val);//从树tree中移除一个结点
Node *max(Node *tree) const; //返回树tree最大项的指针
Node *min(Node *tree) const; //返回树tree最小项的指针
bool contains(Node *tree,const T &val)const;//判断树tree是否包含某项
void clear(Node **tree); //清空树tree
void print(Node *,std::ostream &)const;//打印树tree
Node *clone(Node *tree) const; //复制树tree
};
插入5以前和插入5以后的二叉搜索树
下面是插入结点的实现:
void insert(Node **tree,const T &val){
if(NULL == *tree)
*tree = new Node(val);//树空则生成一个结点
else if(val < (*tree)->data)//左递归
insert(&(*tree)->lchild,val);
else if(val > (*tree)->data)//右递归
insert(&(*tree)->rchild,val);
else{ //树中已经存在结点和待插入的值一样
/* You can do something here*/
}
}
相对于插入操作,删除操作则相对要复杂一些,一旦找到要删除的目标结点,则可能有多种情况:
如果待删除结点是一个叶子结点,则可以直接删除,如果待删除结点有一个儿子,则该结点也可以在简单的调整后删除,如下图所示:
删除结点4前后的二叉搜索树
删除结点2前后的二叉树
void remove(Node **tree,const T &val){
if(NULL == *tree)return;
if(val < (*tree)->data)
remove(&(*tree)->lchild,val);
else if(val > (*tree)->data)
remove(&(*tree)->rchild,val);
else if(NULL != (*tree)->lchild &&NULL != (*tree)->rchild){//待删除结点有两个儿子
(*tree)->data = min((*tree)->rchild)->data; //将待删除结点的值赋值为其右子树的最小值
remove(&(*tree)->rchild,(*tree)->data); //递归删除待删除结点的右子树最小结点
}else{ //找到待删除结点
Node *delNode = *tree;
*tree = ((*tree)->lchild != NULL) ? (*tree)->lchild : (*tree)->rchild;
delete delNode;
}
}
代码实现和测试:
/*
* File: BinarySearchTree.hpp
* User: wqy
* CreateTime: 2013-02-20 21:22
* ModifyTime: 2013-02-20 21:57
*/
#ifndef BINARYSEARCHTREE_H
#define BINARYSEARCHTREE_H
#include <iostream>
#include <vector>
#include <iterator>
template<typename T>
class BSTree{
public:
BSTree():root(NULL),currSize(0){};
explicit BSTree(std::vector<T> &);
BSTree(const BSTree &rhs):root(NULL),currSize(0){*this = rhs;}
BSTree &operator=(const BSTree &);
~BSTree();
T &max() const;
T &min() const;
bool contains(const T &) const;
bool isEmpty() const;
size_t size() const;
void print(std::ostream &out = std::cout) const;
void clear();
void insert(const T &);
void remove(const T &);
private:
struct Node{
T data;
Node *lchild;
Node *rchild;
Node(const T &val,Node *left = NULL,Node *right = NULL):data(val),lchild(left),rchild(right){}
};
Node *root;
size_t currSize;
void insert(Node **tree,const T &val);
void remove(Node **tree,const T &val);
Node *max(Node *tree) const;
Node *min(Node *tree) const;
bool contains(Node *tree,const T &val) const;
void clear(Node **tree);
void print(Node *,std::ostream &) const;
Node *clone(Node *tree) const;
};
template<typename T>
BSTree<T>::BSTree(std::vector<T> &items):root(NULL),currSize(0){
typename std::vector<T>::iterator iter = items.begin();
while(iter != items.end())
insert(*iter++);
}
template<typename T>
BSTree<T>::~BSTree(){
clear();
}
template<typename T>
BSTree<T> &BSTree<T>::operator=(const BSTree<T> &rhs){
if(this != &rhs){
clear();
root = clone(rhs.root);
currSize = rhs.size();
}
return *this;
}
template<typename T>
inline T &BSTree<T>::max() const{
if(!root)
throw std::exception();
return max(root)->data;
}
template<typename T>
inline T &BSTree<T>::min() const{
if(!root)
throw std::exception();
return min(root)->data;
}
template<typename T>
inline bool BSTree<T>::contains(const T &val) const{
return contains(root,val);
}
template<typename T>
inline bool BSTree<T>::isEmpty() const{
return NULL == root;
}
template<typename T>
inline size_t BSTree<T>::size() const{
return currSize;
}
template<typename T>
void BSTree<T>::print(std::ostream &out) const{
print(root,out);
}
template<typename T>
void BSTree<T>::clear(){
clear(&root);
currSize = 0;
}
template<typename T>
void BSTree<T>::insert(const T &val){
if(contains(val)) return;
insert(&root,val);
++currSize;
}
template<typename T>
void BSTree<T>::remove(const T &val){
if(!contains(val))
throw std::exception();
remove(&root,val);
--currSize;
}
template<typename T>
void BSTree<T>::insert(Node **tree,const T &val){
if(NULL == *tree)
*tree = new Node(val);
else if(val < (*tree)->data)
insert(&(*tree)->lchild,val);
else if(val > (*tree)->data)
insert(&(*tree)->rchild,val);
else{
/* You can do something here */
}
}
template<typename T>
void BSTree<T>::remove(Node **tree,const T &val){
if(NULL == *tree) return;
if(val < (*tree)->data)
remove(&(*tree)->lchild,val);
else if(val > (*tree)->data)
remove(&(*tree)->rchild,val);
else if(NULL != (*tree)->lchild && NULL != (*tree)->rchild){
(*tree)->data = min((*tree)->rchild)->data;
remove(&(*tree)->rchild,(*tree)->data);
}else{
Node *delNode = *tree;
*tree = ((*tree)->lchild != NULL) ? (*tree)->lchild : (*tree)->rchild;
delete delNode;
}
}
template<typename T>
inline typename BSTree<T>::Node *BSTree<T>::max(Node *tree) const{
if(NULL == tree)
return NULL;
if(NULL == tree->rchild)
return tree;
return max(tree->rchild);
}
template<typename T>
inline typename BSTree<T>::Node *BSTree<T>::min(Node *tree) const{
if(NULL == tree)
return NULL;
if(NULL == tree->lchild)
return tree;
return min(tree->lchild);
}
template<typename T>
bool BSTree<T>::contains(Node *tree,const T &val) const{
if(NULL == tree) return false;
if(val < tree->data)
return contains(tree->lchild,val);
else if(val > tree->data)
return contains(tree->rchild,val);
else
return true;
}
template<typename T>
void BSTree<T>::clear(Node **tree){
if(*tree != NULL){
clear(&(*tree)->lchild);
clear(&(*tree)->rchild);
delete *tree;
}
*tree = NULL;
}
template<typename T>
void BSTree<T>::print(Node *tree,std::ostream &out) const{
if(tree){
print(tree->lchild,out);
out<<tree->data<<" ";
print(tree->rchild,out);
}
}
template<typename T>
inline typename BSTree<T>::Node *BSTree<T>::clone(Node *tree) const{
if(!tree)
return NULL;
else
return new Node(tree->data,clone(tree->lchild),clone(tree->rchild));
}
#endif
/*
* File: BinarySearchTree.hpp
* User: wqy
* CreateTime: 2013-02-20 22:00
* ModifyTime: 2013-02-20 22:11
*/
#include <iostream>
#include <algorithm>
#include <iterator>
#include <ctime>
#include <cstdlib>
#include "BinarySearchTree.hpp"
using namespace std;
int main(){
BSTree<int> tree1;
srandom((int)time(0));
int i;
for(i = 0;i < 10;++i)
tree1.insert(rand()%100+1);
cout<<"\n########## tree1 ##########"<<endl;
tree1.print();
cout<<endl;
cout<<"size = "<<tree1.size()<<" , min = "<<tree1.min()<<" , max = "<<tree1.max()<<endl;
tree1.insert(55);
cout<<"After insert(55): ";
cout<<(tree1.contains(55) ? "tree1 contains 55" : "tree1 does not contains 55")<<endl;
tree1.clear();
cout<<"After clear(): ";
cout<<(tree1.isEmpty() ? "tree1 empty" : "tree1 not empty")<<endl;
int arr[] = {6,2,8,1,5,3,4};
const int SIZE = sizeof(arr) / sizeof(arr[0]);
vector<int> v;
copy(arr,arr+SIZE,back_inserter(v));
cout<<"\n########## v ##########"<<endl;
copy(v.begin(),v.end(),ostream_iterator<int>(cout," "));
cout<<endl;
BSTree<int> tree2(v);
cout<<"\n########## tree2 #########"<<endl;
tree2.print();
cout<<endl;
tree2.remove(2);
cout<<"After remove(2): ";
tree2.print();
cout<<endl;
BSTree<int> tree3;
tree3 = tree2;
cout<<"\n########## tree3 #########"<<endl;
tree3.print();
cout<<endl;
BSTree<int> tree4(tree3);
cout<<"\n########## tree4 #########"<<endl;
tree4.print();
cout<<endl;
return 0;
}
测试结果: