【理论支持】
算法导论第十二章,二叉查找树
【应用】
二叉查找树是一种很常用的数据结构,太经典,必掌握!它支持很多种动态集合操作.
可用作字典,用作优先队列.
基本操作的时间和树高度成正比,时间复杂度为Θ(lgn)
【我们要实现的操作】
search 查找
minMum 返回最小值
maxMum 返回最大值
predecessor 返回结点x的前驱
successor 返回结点x的后继
insert 插入结点x
delete 删除结点x
【学到的C++基础知识点】
(一)const T& data() const; 定义模版时写的,前后两个const有什么区别,各有什么作用?
(1)前面的const修饰T&
修饰的目的是,虽然返回的是T的引用,但是返回值不能作为<左值>,也就是对于返回值只能读,不能写
(2)后面那个const说明在data()函数内部,不能以任何方式直接或间接的修改成员变量
就是说只能读成员变量而不能写...(get from baidu zhidao)
(二)泛型怎么用?
例如:
template <class T>
struct BinaryNode
{
T element;
BinaryNode *left;
BinaryNode *right;
BinaryNode *parent;
BinaryNode(const T& _element,
BinaryNode *_left,
BinaryNode *_right,
BinaryNode *_parent):
element(_element),
left(_left),
right(_right),
parent(_parent)
{
}
};然后这么使用:BinaryNode<int>* node;
在定义函数的时候,这么使用:
template class<T>
const T& CBinarySearchTree<T>::findMinNum() const
{
BinaryNode<T>* res = findMinNum(m_Root);
return res == NULL?NULL:res->element;
}(三)内存怎么管理
因为不断往树中插入结点,需要insert,自然而然就需要申请内存,怎么申请呢?申请的内存如何释放呢?
(1)申请内存
我们用(二)中的struct BinaryNode来举例,要申请一个BinaryNode的内存空间:用到了构造函数(struct和class都有构造函数)
template class<T>
void CBinarySearchTree<T>::insert(const T& z)
{
//初始化新结点,内存处理...
BinaryNode<T>* newNode = new BinaryNode<T>(z,NULL,NULL,NULL);
//.....
}
(2)释放内存
在CBinarySearchTree类的析构函数中,增加一个public: makeEmpty()和private:makeEmpty(BinaryNode<T>* &m_Root)的函数...
为什么是两个呢,我们设置m_Root是private的,那么在public:makeEmpty()就无法使用m_Root这个变量,只能在private函数中,
增加一个重载的makeEmpty(BinaryNode<T>* &m_Root),然后在makeEmpty()中调用makeEmpty(BinaryNode<T>* &m_Root)...
这机制有点意思:保护了数据~
public:
virtual ~CBinarySearchTree()
{
//内存怎么处理呢??
makeEmpty();
}
void makeEmpty()
{
makeEmpty(m_Root);
}
private:
void makeEmpty(BinaryNode<T>* &m_Root)
{
if (m_Root)
{
makeEmpty(m_Root->left);
makeEmpty(m_Root->right);
delete t;
}
m_Root = NULL;
}
【C++ 代码】
#ifndef CBINARYSEARCHTREE_H
#define CBINARYSEARCHTREE_H
#include <iostream>
#define ORDER_MODE_PREV 0
#define ORDER_MODE_MID 1
#define ORDER_MODE_POST 2
//结点node
template <class T>
struct BinaryNode
{
T element;
BinaryNode *left;
BinaryNode *right;
BinaryNode *parent;
BinaryNode(const T& _element,
BinaryNode *_left,
BinaryNode *_right,
BinaryNode *_parent):
element(_element),
left(_left),
right(_right),
parent(_parent)
{
}
};
template <class T>
class CBinarySearchTree
{
public:
CBinarySearchTree()
{
m_Root = NULL;
}
virtual ~CBinarySearchTree()
{
//内存怎么处理呢??
makeEmpty();
}
void makeEmpty()
{
makeEmpty(m_Root);
}
void insert(const T& z)
{
//初始化新结点,内存处理...
BinaryNode<T>* newNode = new BinaryNode<T>(z,NULL,NULL,NULL);
//x指向准备插入结点,y指向插入结点的父亲
BinaryNode<T>* y = NULL;
BinaryNode<T>* x = m_Root;
while(x != NULL)
{
y = x;
if(z < x->element)
x = x->left;
else
x = x->right;
}
newNode->parent = y;
//边界条件:树为空
if(y != NULL)
{
if(z < y->element)
y->left = newNode;
else
y->right = newNode;
}
else
m_Root = newNode;
}
void remove(const T& z)
{
remove(z, m_Root);
}
//if return NULL :returning reference to temporary...
//how to do it?
const T& findMinNum() const
{
BinaryNode<T>* res = findMinNum(m_Root);
return res == NULL?NULL:res->element;
}
const T& findMaxNum() const
{
BinaryNode<T>* res = findMaxNum(m_Root);
return res == NULL?NULL:res->element;
}
const T& successor(T &x) const
{
BinaryNode<T>* res = successor(x,m_Root);
return res == NULL?NULL:res->element;
}
const T& predecessor(T &x) const
{
BinaryNode<T>* res = predecessor(x,m_Root);
return res == NULL?NULL:res->element;
}
//选择输出顺序,这个真心强大,学习了!
void printTree(int eOrderMode = ORDER_MODE_MID)
{
if (ORDER_MODE_PREV == eOrderMode)
printTreeInPrev(m_Root);
else if (ORDER_MODE_MID == eOrderMode)
printTreeInMid(m_Root);
else if (ORDER_MODE_POST == eOrderMode)
printTreeInPost(m_Root);
else
;
}
const T& search(T &x) const
{
const BinaryNode<T>* res = search(x,m_Root);
return res == NULL?NULL:res->element;
}
private:
void makeEmpty(BinaryNode<T>* &t)
{
if (t)
{
makeEmpty(t->left);
makeEmpty(t->right);
delete t;
}
t = NULL;
}
const BinaryNode<T>* search(const T value,const BinaryNode<T>* x) const
{
if(x == NULL || value == x->element)
return x;
if(value < x->element )
return search(value,x->left);
else
return search(value,x->right);
}
void printTreeInPrev(BinaryNode<T>* x) const
{
if(x == NULL)
return;
std::cout<<x->element<<" ";
printTreeInPrev(x->left);
printTreeInPrev(x->right);
}
void printTreeInMid(BinaryNode<T>* x) const
{
if(x == NULL)
return;
printTreeInMid(x->left);
std::cout<<x->element<<" ";
printTreeInMid(x->right);
}
void printTreeInPost(BinaryNode<T>* x) const
{
if(x == NULL)
return;
printTreeInPost(x->left);
printTreeInPost(x->right);
std::cout<<x->element<<" ";
}
void remove(const T& value, BinaryNode<T>* z)
{
if(z == NULL)
return;
if(value < z->element)
remove(value,z->left);
else if(value > z->element)
remove(value,z->right);
else if(value == z->element)
{
//根据z的孩子数目,我们要删除的结点y,不一样
//当没孩子或者只有一个孩子,y就是z本身
//两个孩子,y就是z的后继
BinaryNode<T>* y = NULL;
if(z->left != NULL && z->right != NULL)
y = successor(z->element,z);
else
y = z;
//x为孩子结点,从左孩子起,没有就为右孩子,再没有就NULL
BinaryNode<T>* x = NULL;
if(y->left != NULL)
x = y->left;
else
x = y->right;
//删除y结点就是把y孩子(x结点)接到y->parent上
if(x != NULL)
x->parent = y->parent;
//边界条件:y为root结点,那么y->parent就是NULL啦。
if(y->parent != NULL)
{
if(y == y->parent->left)
{
y->parent->left = x;
}
else if(y == y->parent->right)
{
y->parent->right = x;
}
}
else
{
m_Root = x;
}
if(y != z)
{
z->element = y->element;
}
//算法导论就没内存概念吗?
delete y;
}
else
;
}
BinaryNode<T>* findMinNum( BinaryNode<T>* x) const
{
while(x->left != NULL)
x = x->left;
return x;
}
BinaryNode<T>* findMaxNum( BinaryNode<T>* x) const
{
while(x->right != NULL)
x = x->right;
return x;
}
BinaryNode<T>* successor(const T value,const BinaryNode<T>* x) const
{
if(x == NULL)
return NULL;
if(value < x->element)
successor(value,x->left);
else if(value > x->element)
successor(value,x->right);
else
{
//如果有右孩子,直接返回大于自己的最小的元素
if(x->right != NULL)
return findMinNum(x->right);
//找到"父结点为祖父结点的左孩子"的那个祖父结点
BinaryNode<T>* y = NULL;
y = x->parent;
while(y != NULL && x == y->right)//找到x为y左孩子为止
{
x = y;
y = y->parent;
}
return y;
}
return NULL;
}
BinaryNode<T>* predecessor(const T value, const BinaryNode<T>* x)const
{
if(x == NULL)
return NULL;
if(value < x->element)
predecessor(value,x->left);
else if(value > x->element)
predecessor(value,x->right);
else
{
//如果有左孩子,直接返回小于自己的最大的元素
if(x->left != NULL)
return findMaxNum(x->left);
//找到"父结点为祖父结点的右孩子"的那个祖父结点
BinaryNode<T>* y = NULL;
y = x->parent;
while(y != NULL && x == y->left)//找到x为y左孩子为止
{
x = y;
y = y->parent;
}
return y;
}
return NULL;
}
protected:
private:
BinaryNode<T>* m_Root;
};
#endif // CBINARYSEARCHTREE_H
【C++测试代码】
#include <iostream>
#include "CBinarySearchTree.h"
#include "stdlib.h"
using namespace std;
int main()
{
CBinarySearchTree<int> bst;
//test for insert
bst.insert(1);
bst.insert(2);
bst.insert(3);
bst.insert(4);
bst.insert(5);
//test for printTree 有三种模式,分别为0,1,2
cout<<"prev order: ";
bst.printTree(ORDER_MODE_PREV);
cout<<endl;
cout<<"min order: ";
bst.printTree(ORDER_MODE_MID);
cout<<endl;
cout<<"post order: ";
bst.printTree(ORDER_MODE_POST);
cout<<endl;
//test for findMaxNum()
cout<<"Max number: "<<bst.findMaxNum()<<endl;
//test for findMinNum()
cout<<"Min number: "<<bst.findMinNum()<<endl;
//teft for successor()
for(int i = 1 ; i <= 5 ;i++)
cout<<"node "<<i<<" successor: "<<bst.successor(i)<<endl;
//test for predecessor()
for(int i = 1 ; i <= 5 ;i++)
cout<<"node "<<i<<" predecessor: "<<bst.predecessor(i)<<endl;
//test for search()
for(int i = 1 ; i <= 5 ;i++)
cout<<"node "<<i<<" search: "<<bst.search(i)<<endl;
//test for remove()
for(int i = 0 ; i < 5 ;i++)
{
bst.remove(i+1);
cout<<"after delete "<<i+1<<": ";
bst.printTree(ORDER_MODE_MID);
cout<<endl;
}
return 0;
}
【C++ 测试结果】
prev order: 1 2 3 4 5
min order: 1 2 3 4 5
post order: 5 4 3 2 1
Max number: 5
Min number: 1
node 1 successor: 2
node 2 successor: 0
node 3 successor: 0
node 4 successor: 0
node 5 successor: 0
node 1 predecessor: 0
node 2 predecessor: 0
node 3 predecessor: 0
node 4 predecessor: 0
node 5 predecessor: 0
node 1 search: 1
node 2 search: 2
node 3 search: 3
node 4 search: 4
node 5 search: 5
after delete 1: 2 3 4 5
after delete 2: 3 4 5
after delete 3: 4 5
after delete 4: 5
after delete 5:
Process returned 0 (0x0) execution time : 0.546 s
Press any key to continue.