二叉树是结构最简单的树了,而二叉查找树又是特殊的二叉树,它能实现高效的查找,删除,插入等基本操作,STL里的map与set就是用了自平衡的二叉查找树的思想实现的
不过我这里先不实现自平衡功能,但这又是很重要的,等下我会说明,现在还是先看看二叉查找树的实现:
首先是Btree头文件:
#ifndef BTREE_H
#define BTREE_H
#include<iostream>
using namespace std;
template <class Comparable> //必须是可比较大小的数据类型
class Btree
{
public:
Btree(){root=NULL;}
Btree(const Btree &rhs)
{
root=NULL;
*this=rhs;
}
~Btree()
{
makeEmpty();
}
const Comparable & findMin()const
{
return findMin(root)->element;
}
const Comparable & findMax()const
{
return findMax(root)->element;
};
bool contains(const Comparable &x)const//查找;
{
return contains(x,root);
}
bool isEmpty()const
{
return root==NULL;
}
void printTree()
{
printTree(root);
}
void display()
{
display(root);
}
void makeEmpty()
{
makeEmpty(root);
}
void insert(const Comparable &x)
{
insert(x,root);
}
void remove( Comparable &x)
{
remove(x,root);
}
const Btree & operator=(const Btree &rhs)
{
if(this!=&rhs)
{
makeEmpty();
root=clone(rhs.root);
}
return *this;
};
friend ostream & operator <<(ostream &out,Btree<Comparable> &T)
{
T.printTree();
return out;
}
Btree & operator -(Btree &T)
{
while(!T.isEmpty())
{
this->remove(T.root->element);
T.remove(T.root->element);
}
return *this;
}
private:
struct BNode
{
Comparable element;
BNode *left;
BNode *right;
BNode(const Comparable &ele,BNode *lt=NULL,BNode *rt=NULL)
:element(ele),left(lt),right(rt){}
};
BNode *root;
void insert(const Comparable &x,BNode *&T)const
{
if(T==NULL)
T=new BNode(x);
BNode *t=T,*tt=NULL; //非递归实现
while(t!=NULL)
{
tt=t;
if(x<t->element)
t=t->left;
else if(t->element<x)
t=t->right;
else
return;
}
t=new BNode(x);
if(tt->element>x)
tt->left=t;
else
tt->right=t;
/*else if(x<=t->element) //递归实现
insert(x,t->left);
else if(x>t->element)
insert(x,t->right);
*/
}
void remove(Comparable &x,BNode * &T)const
{
if(T==NULL)
return ;
BNode *t=T,*tt=NULL; //非递归实现
while(t!=NULL)
{
if(x<t->element)
{tt=t;t=t->left;}
else if(x>t->element)
{tt=t; t=t->right;}
else if(t->left !=NULL && t->right !=NULL)
{
x=t->element=findMin(t->right)->element;
tt=t;
t=t->right;
}
/*if(x<t->element) //递归实现
{
remove(x,t->left);
}
else if(x>t->element)
{
remove(x,t->right);
}
else if(t->left !=NULL && t->right !=NULL)
{
t->element=findMin(t->right)->element;
remove(t->element,t->right);
}*/
else
{
BNode *oldnode=t;
t=(t->left!=NULL)?t->left:t->right;
delete oldnode;
break;
}
}
if(tt==NULL)
{
T=t;
return;
}
else if(tt->element>x)
tt->left=t;
else
tt->right=t;
}
BNode *findMin(BNode *t)const
{
if(t==NULL)
return NULL;
if(t->left==NULL)
return t;
return findMin(t->left);
}
BNode *findMax(BNode *t)const
{
if(t==NULL)
return NULL;
if(t->right==NULL)
return t;
return findMax(t->right);
}
bool contains(const Comparable &x,BNode *t)const//查找;
{
if(t==NULL)
return false;
else if(x<t->element)
return contains(x,t->left);
else if(x>t->element)
return contains(x,t->right);
else
return true;
}
void makeEmpty(BNode * &t)
{
if(t!=NULL)
{
makeEmpty(t->left);
makeEmpty(t->right);
delete t;
}
t=NULL;
}
void printTree(BNode * t)const
{
if(t!=NULL)
{
printTree(t->left);
cout<<t->element<<" ";
printTree(t->right);
}
}
void display(BNode * t,int n=0)const
{
if(t!=NULL)
{
display(t->left,n+1);
for(int i=1;i<=n;i++)
cout<<" ";
cout<<t->element<<endl;
display(t->right,n+1);
}
}
BNode *clone(BNode *t)const
{
if(t==NULL)
return NULL;
return new BNode(t->element,clone(t->left),clone(t->right));
}
};
#endif
可以看到,唯一的难点在于插入和删除,其实如果用递归实现的话,还是很简单,但节点一多就可能会出现栈溢出的可能,而且递归效率原本就没有循环的效率高。
然后是测试代码:
#include"Btree.h"
#include<cstdlib>
#include<ctime>
#include<cstdio>
int main(){
freopen("out.txt","w",stdout);
Btree<int> b;
srand(time(NULL));
int n;
cin>>n;
clock_t start,finish;
start=clock();
for(int i=0;i<n;i++)
{
b.insert(rand()%n);
}
finish=clock();
cout<<double(finish-start)/CLOCKS_PER_SEC <<"s"<<endl;
b.printTree();
b.display();
Btree<int> t(b);
cout<<endl;
for(int i=0;i<=n/2;i++)
t.remove(i);
cout<<t<<endl;
return 0;
fclose(stdout);
}
输入100后的结果如下:
0.000104s
1 2 4 5 6 7 8 9 10 11 13 14 15 18 21 23 24 25 27 30 33 34 36 38 39 41 43 46 48 51 54 56 57 58 59 60 62 63 64 66 67 68 70 71 74 75 76 77 78 79 80 81 83 84 85 86 87 88 89 90 91 92 93 96 99 1
2
4
5
6
7
8
9
10
11
13
14
15
18
21
23
24
25
27
30
33
34
36
38
39
41
43
46
48
51
54
56
57
58
59
60
62
63
64
66
67
68
70
71
74
75
76
77
78
79
80
81
83
84
85
86
87
88
89
90
91
92
93
96
99
51 54 56 57 58 59 60 62 63 64 66 67 68 70 71 74 75 76 77 78 79 80 81 83 84 85 86 87 88 89 90 91 92 93 96 99
中间输出的是树的大致样子,现在就可以来讲讲为什么自平衡那么重要了,假设我们是按照数的大小顺序来插入的话如从1插到100,那么树就有100层,这时无论做什么操作,都相当与链表,开销很大,都是O(n)的,所以我们一定要想办法让二叉查找树具有自平衡的功能!