首先了解一下二叉树的一些基本操作,二叉树支持插入,删除,遍历的操作。第一个安插至空白树的值,会成为此树的根节点。接下来的每个节点按特定的规则插入。如果小于根节点,就被置于左侧指数,大于根节点就被置于右子树。string类型按照字典排序。如下图
遍历又分前序遍历,中序遍历,后序遍历。
按照上图,前序遍历结果: Piglet,Ek,Chris,Kanga,Roo,Pooh,Trigger.
中序遍历结果:Chris Ek Kanga Piglet Pooh Roo Trigger
后序遍历结果:Chris Kanga Ek Pooh Trigger Roo Piglet
下面先实现一个节点类型BTnode。如果不实现泛型,
class string_node { public: private: string _val; //节点的值 int _cnt; //节点计数 string_node *_lchild; //左节点 string_node *_rchild; //右节点 };
如果要实现存储int类型的节点则又要定义一个int_node类。这显然太麻烦。我们可以定义一个支持泛型的节点。
template<typename valType> class BTnode { friend class BinaryTree<valType>; //把二叉树类型BinaryTree声明为友元类,这样BinaryTree就可以访问BTnode的私有成员 _val,_cnt,_lchild,_rchild等 public: BTnode(){} BTnode(const valType &val); void insert_value(const valType& elem); void remove_value( const valType &val, BTnode *& prev); static void lchild_leaf( BTnode *leaf, BTnode *subtree); private: valType _val; int _cnt; BTnode *_lchild; BTnode *_rchild; };
为了通过class template产生实体类,我们必须在class tempalte名称之后,紧接一个尖括号,其内放置一个实际类。例如:BTnode<int> 则将valType绑定至int, BTnode<string>则讲valType绑定至string。这样我们就实现了泛型。没有必要再为
每个类型都定义一个节点类型了。什么情况下我们需要 模板参数列表(template parameter list)去修饰 模板类(class template)呢。 一般的规则是,在class template 以及其members的定义式中,不需要之外。其他的场合都需要以parameter list 加以修饰。如:
template<typename elemType> class BinaryTree { public: ... private: BTnode<elemType> *_root; };下面给出BTnode完整的定义:
template<typename Type> class BinaryTree; template<typename valType> class BTnode { friend class BinaryTree<valType>; public: BTnode(){} BTnode(const valType &val); void insert_value(const valType& elem); void remove_value( const valType &val, BTnode *& prev); static void lchild_leaf( BTnode *leaf, BTnode *subtree); private: valType _val; int _cnt; BTnode *_lchild; BTnode *_rchild; }; template<typename valType> BTnode<valType>::BTnode(const valType &val) : _val(val) { _cnt = 1; _lchild = _rchild = 0; } template<typename valType> void BTnode<valType>::insert_value(const valType &val) { if ( this->_val == val) { this->_cnt++; return ; } if(this->_val > val ) { if(!this->_lchild) this->_lchild = new BTnode<valType>(val); else this->_lchild->insert_value(val); } else { if(!this->_rchild) this->_rchild = new BTnode<valType>(val); else this->_rchild->insert_value(val); } } template<typename valType> void BTnode<valType>::remove_value( const valType &val, BTnode *& prev) { //找到相应的值,删除该节点。prev是起始的节点。 这里需要修改BTnode *指针本身,所以我们定义为 BTnode *& prev if( val < _val ) { if ( !_lchild) return; else _lchild->remove_value(val, _lchild); } else if ( val > _val) { if( !_rchild) return; else _rchild->remove_value(val,_rchild); } else { if (_rchild) { prev = _rchild; if(_lchild) if( !prev->_lchild) prev->_lchild = _lchild; else BTnode<valType>::lchild_leaf(_lchild,prev->_lchild); } else prev = _lchild; delete this; } } template<typename valType> inline void BTnode<valType>::lchild_leaf( BTnode *leaf, BTnode *subtree) { //使leaf成为subtree的左子树的叶子节点 while (subtree->_lchild) subtree = subtree->_lchild; subtree->_lchild = leaf; }
template<typename valType> BTnode<valType>::BTnode(const valType &val) : _val(val) { _cnt = 1; _lchild = _rchild = 0; }为什么这里第二次出现BTnode的时候不需要<valType>去修饰了呢,因为在class scope运算符出现之后 BTnode<valType>::,其后所有东西被视为位于class定义域内:还记得上面所说的规则吗在class template 以及其members的定义式中,不需要之外。其他的场合都需要以parameter list 加以修饰。
BTnode<valType>:: //在class定义域之外。
BTnode() //在class定义域之内。
关于函数参数的规则是,若是非基本类型,则使用传址的方式(by reference)传递 ,如果这个参数确认了,在函数内是只读的则加上const 修饰词。如:
insert_value(const valType &val)
下面给出BinaryTree的模板实现:
template<typename elemType> class BinaryTree { public: BinaryTree(); BinaryTree(const BinaryTree&); ~BinaryTree(); BinaryTree& operator= (const BinaryTree&); void insert( const elemType &); bool empty() { return _root == 0;} void remove(const elemType &elem); void remove_root(); void clear() { if(_root) { clear(_root); _root = 0;}} void preorder(); void preorder(BTnode<elemType> *node, ostream &os = cout); static ostream & display_val( elemType &node,ostream &os = cout); void pre_recursion(BTnode<elemType> *node); BTnode<elemType>* get_root() { return _root;} private: BTnode<elemType> *_root; void clear(BTnode<elemType> *node); void copy(BTnode<elemType> *tar, BTnode<elemType> *src); }; template<typename elemType> inline BinaryTree<elemType>:: BinaryTree() : _root(0) {} template<typename elemType> inline BinaryTree<elemType>::BinaryTree(const BinaryTree& rhs) { copy(_root,rhs._root); } template<typename elemType> void BinaryTree<elemType>::insert( const elemType &elem) { if (!_root) _root = new BTnode<elemType>(elem); _root->insert_value(elem); } template<typename elemType> inline BinaryTree<elemType>::~BinaryTree() { clear(); } template<typename elemType> inline BinaryTree<elemType>& BinaryTree<elemType>::operator= (const BinaryTree &rhs) { if( ! this = &rhs) { clear(); copy(_root,rhs._root); } return *this; } template<typename elemType> inline void BinaryTree<elemType>::remove( const elemType &elem) { if(_root) { if( _root->_val == elem) remove_root(); else _root->remove_value(elem, _root); } } template<typename elemType> void BinaryTree<elemType>:: remove_root() { if (!_root) return; BTnode<elemType> *tmp = _root; if( !_root->_rchild) { _root = _root->_rchild; if(tmp->_lchild) { if(!_root->_lchild) //没有任何子树则直接接上 _root->_lchild = tmp->_lchild; else BTnode<elemType>::lchild_leaf(tmp->_lchild,_root->_lchild); } } else _root = _root->_lchild; delete tmp; } //清除所有节点 template<typename elemType> void BinaryTree<elemType>::clear(BTnode<elemType> *node) { if(node) { clear(node->_lchild); clear(node->_rchild); delete node; } } template<typename elemType> void BinaryTree<elemType>::preorder() { pre_recursion(_root); } //递归的前序遍历 template<typename elemType> void BinaryTree<elemType>::preorder(BTnode<elemType> *node, ostream &os) { if(node) { display_val(node->_val,os); preorder(node->_lchild,os); preorder(node->_rchild,os); } } template<typename elemType> ostream & BinaryTree<elemType>::display_val(elemType &node , ostream &os) { os << node << ' '; return os; } //非递归实现前序遍历 template<typename elemType> void BinaryTree<elemType>::pre_recursion (BTnode<elemType> *node) { stack<BTnode<elemType>*> s; //使用先进后出栈 s.push(node); while(!s.empty()) { BTnode<elemType>* tmp = s.top(); s.pop(); BinaryTree<elemType>::display_val(tmp->_val,std::cout); if(tmp->_rchild) s.push(tmp->_rchild); //右节点先进栈 后出,后遍历 if(tmp->_lchild) s.push(tmp->_lchild); //左节点后进栈,先出,先遍历 } }
测试:
int main() { BinaryTree<string> bt; bt.insert("abc"); bt.insert("agcb"); bt.insert("kfgd"); bt.insert("how are you"); bt.preorder(); //bt.remove("abc"); //bt.preorder(); bt.remove("kfgd"); bt.preorder(); return 0; }本章不仅让我了解泛型编程,模板类是怎么一回事,template的语法。而且还让我重温了一次二叉排序树 这个数据结构。
参考文献:
《Eseential C++》