博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
平衡二叉树的C++模板实现
阅读量:2394 次
发布时间:2019-05-10

本文共 6226 字,大约阅读时间需要 20 分钟。

本文内容为前天写的AVL树模板的C++代码实现。本想把二叉搜索树设计成基类(SearchTree),然后由AVL树和红黑树分别对其继承,因为作为搜索树,它们在查找、插入和删除元素时都用到了相同的节点查找方法,因此这种继承机制可以很好地利用继承机制的优点。但是在类的具体实现时,却遇到了一些问题。

首先,我定义了二叉搜索树的树节点类:BTNode。它只是普通的二叉树节点:数据成员包括value和两个指针lchild、rchild分别指向左右孩子。但是这里的指针只能定义为指向基类(BTNode)的指针。然后定义了SearchTree类。该类没有自己的数据成员,仅为子类提供接口。共有两个虚函数:

virtual BTNode<T>* root_insert(const T& elem, BTNode<T> *root) ;

virtual BTNode<T>* root_find(const T& elem, BTNode<T> *root);

分别用于定位元素的父节点和定位元素。函数的参数以及返回值包含指向树节点的指针,在该类的实现中,指针也只能是指向基类(BTNode)节点的指针。

然后定义AVL树的节点类AVLTNode,继承BTNode,扩充了两个数据成员:指向父节点的指针parent(为派生类指针)和表示平衡因子的整数BF。

接下来定义了AVL树,数据成员为指向AVLTNode的指针root表示树根。问题就出现在AVLTree的实现中。在实现插入和删除时用到了基类的成员函数实现节点查找,找到的是指向基类节点的指针;节点的左右子节点指针均为指向基类的指针。在这里就需要用到大量的数据类型转换以实现指向基类的指针为指向派生类的指针的赋值。还好在这里我们可以保证AVL中的树节点均为派生类节点而不至于引起派生类指针指向基类对象所引发的不可预知的结果。下面的代码中可以发现大量的static_cast关键字,原因就在这里。。。为了减少static_cast的使用,可以再派生类中实现一个内联函数来实现。

/

以下就是具体的代码实现:

// BTNode类的定义
template
 
class BTNode{	public:		BTNode():lchild(0), rchild(0){}		BTNode(T elem):data(elem), lchild(0), rchild(0){}		virtual ~BTNode()		{		}	protected:		BTNode
*lchild; BTNode
*rchild; T data; private: template
friend class SearchTree; template
friend class AVLTree;};
// AVLTNode类的定义template 
class AVLTNode : public BTNode
{ public: AVLTNode():parent(0), BF(0){} AVLTNode(T elem) : BTNode
(elem), parent(0), BF(0){} AVLTNode
* right_rotate(); AVLTNode
* left_rotate(); private: AVLTNode
*parent; int BF; private: template
friend class AVLTree;};template
AVLTNode
* AVLTNode
::right_rotate() // 右旋,仅实现了子树内部父子关系的改变以及BF值的更新,子树与外部节点之间的父子关系还未更新{ AVLTNode
*p(static_cast
*>(this->lchild)); if(!p) { return this; } this->lchild = p->rchild; if(p->rchild) { static_cast
*>(p->rchild)->parent = this; } p->rchild = this; BF -= (p->BF > 0 ? 1 + p->BF : 1); p->BF -= (BF < 0 ? 1 - BF : 1); return p;}template
AVLTNode
* AVLTNode
::left_rotate(){ AVLTNode
*p(static_cast
*>(this->rchild)); if(!p) { return this; } this->rchild = p->lchild; if(p->lchild) { static_cast
*>(p->lchild)->parent = this; } p->lchild = this; BF += (p->BF < 0 ? 1 - p->BF : 1); p->BF += (BF > 0 ? 1 + BF : 1); return p;}template
class SearchTree{ public: SearchTree() {} virtual ~SearchTree() {} protected: virtual BTNode
* root_insert(const T& elem, BTNode
*root); virtual BTNode
* root_find(const T& elem, BTNode
*root); private:};template
BTNode
* SearchTree
::root_insert(const T& elem, BTNode
*root){ BTNode
*p(root); while(p) { root = p; p = (root->data < elem ? root->rchild : root->lchild); } return root;}template
BTNode
* SearchTree
::root_find(const T& elem, BTNode
*root){ while(root) { if(root->data == elem) { break; } else if(root->data < elem) { root = root->rchild; } else { root = root->lchild; } } return root;}
// 以下为AVLTree模板的代码实现:
template 
class AVLTree : public SearchTree
{ public: AVLTree() : root(0) {} virtual ~AVLTree() {} virtual void insert(const T& elem); virtual void erase(const T& elem); virtual bool find(const T& elem); protected: private: AVLTNode
*root;};
// 插入操作,也许会改变父节点的BF值,自新插入节点向上追溯并更新相应节点的BF值直到根节点,若某个节点BF变为2或-2,则需要旋转以达到平衡,且向上的追溯止于此。template 
void AVLTree
::insert(const T& elem){ AVLTNode
*p = static_cast
*>(SearchTree
::root_insert(elem, root)); AVLTNode
*node = new AVLTNode
(elem); node->parent = p; if(!p) { root = node; return; } if(p->data >= elem) { p->lchild = node; } else { p->rchild = node; } while(p) { if(node == p->lchild) { ++p->BF; if(p->BF == 0) { break; } if(p->BF == 2) { if(node->BF == -1) { AVLTNode
*q = node->left_rotate(); q->parent = p; node->parent = q; p->lchild = q; node = q; } node = p->right_rotate(); node->parent = p->parent; p->parent = node; if(!node->parent) { root = node; } else { AVLTNode
*q = node->parent; if(q->lchild == p) { q->lchild = node; } else { q->rchild = node; } } break; } node = p; p = p->parent; } else { --p->BF; if(p->BF == 0) { break; } if(p->BF == -2) { if(node->BF == 1) { AVLTNode
*q = node->right_rotate(); q->parent = p; node->parent = q; p->rchild = q; node = q; } node = p->left_rotate(); node->parent = p->parent; p->parent = node; if(!node->parent) { root = node; } else { AVLTNode
*q = node->parent; if(q->lchild == p) { q->lchild = node; } else { q->rchild = node; } } break; } node = p; p = p->parent; } }}
// 删除操作,父节点的BF会变化,故同样需要向上追溯。若BF由非0变为0,则树高减小,继续向上追溯;否则停止追溯。template 
void AVLTree
::erase(const T& elem){ BTNode
*fp = SearchTree
::root_find(elem, root); if(!fp) { return; } BTNode
*fnode = fp->lchild; if(!fnode) { fnode = (fp->rchild ? fp->rchild : fp); } else { while(fnode->rchild) { fnode = fnode->rchild; } } fp->data = fnode->data; AVLTNode
*p = static_cast
*>(fnode); AVLTNode
*q = p->parent; AVLTNode
*node = static_cast
*>(p->lchild); if(root == p) { delete p; root = 0; return; } if(node) { node->parent = q; } bool Left(true); if(p == q->lchild) { q->lchild = node; } else { Left = false; q->rchild = node; } delete p; p = node; while(q) { if(Left) { int BF = --q->BF; if(BF == -1) { break; } else if(BF == -2) { node = static_cast
*>(q->rchild); if(node->BF == 1) { AVLTNode
*temp = node->right_rotate(); temp->parent = q; node->parent = temp; q->rchild = temp; } p = q->left_rotate(); p->parent = q->parent; q->parent = p; node = p->parent; if(!node) { root = p; break; } if(q == node->lchild) { node->lchild = p; } else { node->rchild = p; } q = node; } else { p = q; q = q->parent; } } else { int BF = ++q->BF; if(BF == 1) { break; } else if(BF == 2) { node = static_cast
*>(q->lchild); if(node->BF == -1) { AVLTNode
*temp = node->left_rotate(); temp->parent = q; node->parent = temp; q->lchild = temp; } p = q->right_rotate(); p->parent = q->parent; q->parent = p; node = p->parent; if(!node) { root = p; break; } if(q == node->lchild) { node->lchild = p; } else { node->rchild = p; } q = node; } else { p = q; q = q->parent; } } if(q) { Left = (p == q->lchild); } }}template
bool AVLTree
::find(const T& elem){ return SearchTree
::root_find(elem, root) != NULL;}

转载地址:http://nyzob.baihongyu.com/

你可能感兴趣的文章
Java编程思想学习笔记(12)
查看>>
Java编程思想学习笔记(13)
查看>>
Java编程思想学习笔记(14)
查看>>
Java-8-UnaryOperator
查看>>
Java-8-Function
查看>>
Java-8-Stream接口
查看>>
Junit4入门
查看>>
Java与算法(11)
查看>>
Java与算法(13)
查看>>
Python时间模块
查看>>
Python的闭包和装饰器
查看>>
Python基于Socket实现简单聊天室
查看>>
Python的Twisted入门
查看>>
Flask的表单处理
查看>>
Flask-Login的使用
查看>>
Python往MySQL存储图片
查看>>
Flask-SQlAIchemy管理数据库
查看>>
Flask-Migrate实现数据库迁移
查看>>
su: cannot set user id: Resource temporarily unavailable
查看>>
SSHException: Incompatible ssh peer (no acceptable kex algorithm)
查看>>