【理论支持】
算法导论第十二章,二叉查找树
【应用】
二叉查找树是一种很常用的数据结构,太经典,必掌握!它支持很多种动态集合操作.
可用作字典,用作优先队列.
基本操作的时间和树高度成正比,时间复杂度为Θ(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; }
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.