歡迎來到Linux教程網
Linux教程網
Linux教程網
Linux教程網
Linux教程網 >> Linux編程 >> Linux編程 >> C++模板實現的AVL樹

C++模板實現的AVL樹

日期:2017/3/1 9:36:16   编辑:Linux編程

1 AVL樹的定義

AVL樹是一種自平衡二叉排序樹,它的特點是任何一個節點的左子樹高度和右子樹的高度差在-1,0,1三者之間。AVL樹的任何一個子樹都是AVL樹。

2 AVL樹的實現

AVL樹本質是一種二叉排序樹,所以二叉排序樹的任何性質AVL樹都具有,但是AVL樹稍微復雜的地方就是AVL樹必須滿足平衡條件,具體跟BST不同的地方主要體現在插入,刪除操作。

插入操作:當插入之後可能會出現不平衡,所以這時候要通過旋轉樹來實現平衡。旋轉有四種類型,左左,左右,右左,右右。其中左左旋轉和右右旋轉是鏡像的,左右旋轉和右左旋轉是鏡像的,所以實質上就是兩種類型的旋轉。針對左左旋轉,只需要旋轉一次即可,針對左右旋轉,需要執行兩次旋轉。見下圖:

這裡采用遞歸法實現插入和刪除操作。使用遞歸方便的一點是如果函數的參數是引用類型的,當傳入一個p->left的時候,我們在當前函數的下層遞歸的時候,對p進行的賦值操作其實就是對上層遞歸中的p->left進行的操作,所以這樣就不需要傳遞父指針了。

3 實現代碼

//AVLTree.h

#ifndef DDXX_AVLTREE_H
#define DDXX_AVLTREE_H
#include <iostream>
#include <queue>
using namespace std;
template<typename Type>
class AVLTree
{
struct Node
{
Type e;
Node* left;
Node* right;
int h;
Node(Type _e):e(_e),left(NULL),right(NULL),h(0){}
Node(Type _e,Node* _left,Node* _right,int _h):e(e),left(_left),right(_right),h(_h){}
};
public:
AVLTree();
AVLTree(Type arr[],int nLength);
/*AVLTree(const AVLTree& right);
AVLTree& operator=(const AVLTree& right);*/
~AVLTree();
public:
bool insert(Type e,Node* &p);
void erase(Type e,Node* &p);
Node*& find(Type e)const;
void traverse(Node* p)const;
void traverseByLevel(Node* p)const;
int getLength(){return mLength;}
Node*& getParent(Node* p);
Node*& getRoot(){return mRoot;} //notice the return type
bool empty(){return mRoot==NULL;};
void clear();
void clears(Node* &p);
private:
void rotateLeft(Node* &k2);
void rotateRight(Node* &k2);
void rotateLeftDouble(Node* &p);
void rotateRightDouble(Node* &p);
int height(Node* p)const{ return p == NULL ? -1 : p->h ;}
int max(int x,int y){return x>y?x:y;}
private:
Node* mRoot;
int mLength;

};
template<typename Type> AVLTree<Type>::AVLTree():mRoot(NULL),mLength(0)
{
}

template<typename Type> AVLTree<Type>::AVLTree(Type arr[],int nLength):mRoot(NULL),mLength(0)
{
for(int i=0;i<nLength;i++)
{
insert(arr[i],mRoot);
}
}

template<typename Type> AVLTree<Type>::~AVLTree()
{
clears(mRoot);
}
template<typename Type> bool AVLTree<Type>::insert(Type e,Node* &p)
{
if( p== NULL)
{
p = new Node(e);
mLength++;
}
else if(e < p->e)
{
insert(e,p->left);
if( height(p->left) - height(p->right) == 2)
{
if (e < p->left->e)
rotateLeft(p);
else
rotateLeftDouble(p);
}
}
else if(e > p->e)
{
insert(e,p->right);
if( height(p->left) - height(p->right) == -2)
{
if (e > p->right->e)
rotateRight(p);
else
rotateRightDouble(p);
}
}
else // e ia already exist
{
//return false;
}
p->h = max( height(p->left),height(p->right) )+1;
return true;
}

template<typename Type> void AVLTree<Type>::rotateLeft(Node*& k2)
{
Node* k1 = k2->left;
k2->left = k1->right;
k1->right = k2;

k1->h = max( height(k1->left),height(k1->right) ) + 1;
k2->h = max( height(k2->left),height(k2->right) ) + 1;
k2 = k1;// join the original node
}

template<typename Type> void AVLTree<Type>::rotateRight(Node* &k2)
{
Node* k1 = k2->right;
k2->right = k1->left;
k1->left = k2;

k1->h = max( height(k1->left),height(k1->right) ) + 1;
k2->h = max( height(k2->left),height(k2->right) ) + 1;
//k1=k2,因為在insert函數中傳入的是p->left或者p->right的引用,所以這裡能把根結點賦給其父結點的子節點
k2 = k1;
}

template<typename Type> void AVLTree<Type>::rotateLeftDouble(Node*& k3)
{
rotateRight(k3->left);
rotateLeft(k3);
}
template<typename Type> void AVLTree<Type>::rotateRightDouble(Node*& k3)
{
rotateLeft(k3->right);
rotateRight(k3);
}

template<typename Type> void AVLTree<Type>::traverse(Node* p)const
{
if( p == NULL)
return;
else
{
traverse(p->left);
cout<<"element:"<<p->e<<endl; //traverse by mid
traverse(p->right);
}
}

template<typename Type> void AVLTree<Type>::traverseByLevel(Node* root)const
{
if(root == NULL)
{
cout<<"The tree is empty"<<endl;
return;
}
queue<Node*> que;
que.push(root);
while( !que.empty() )
{
Node* ptr = que.front();
que.pop();
cout<<"element:"<<ptr->e<<" th:"<<height(ptr->left) - height(ptr->right)<<endl;
if(ptr->left != NULL)
que.push(ptr->left);
if(ptr->right != NULL)
que.push(ptr->right);
}
}

template<typename Type> typename AVLTree<Type>::Node* & AVLTree<Type>::getParent(Node* p)
{
if( p == m_root)
return NULL;
Node* ptr = m_root;
Node* ptf = ptr;
while( ptr != NULL )
{
if ( ptr->e == p->e )
return ptf;
if ( ptr->e > p->e )
{
ptf = ptr;
ptr = ptr->leftChild;
}
else
{
ptf = ptr;
ptr = ptr->rightChild;
}
}
}

template<typename Type> typename AVLTree<Type>::Node*& AVLTree<Type>::find(Type e)const
{
Node* ptr = m_root;

while(ptr != NULL)
{
if ( ptr->e == e )
return ptr;
if ( ptr->e > e )
ptr = ptr->leftChild;
else
ptr = ptr->rightChild;
}
//if ( ptr == NULL )
return NULL;
}

template<typename Type> void AVLTree<Type>::clears(Node*& p)
{
if( p == NULL )
return;
else
{
clears(p->left);
clears(p->right);
delete p;
p = NULL;
mLength--;
}
}

template<typename Type> void AVLTree<Type>::clear()
{
clears(mRoot);
}

template<typename Type> void AVLTree<Type>::erase(Type e,Node* &p)
{
if( p == NULL)
return;
if( e > p->e)
{
erase(e,p->right);
if( height(p->left) - height(p->right) == 2)
{
if( height(p->left->left) > height(p->left->right) )
rotateLeft(p);
else
rotateLeftDouble(p);
}
}
else if( e < p->e)
{
erase(e,p->left);
if( height(p->left) - height(p->right) == -2)
{
if( height(p->right->right) > height(p->right->left) )
rotateRight(p);
else
rotateRightDouble(p);
}
}
else if ( e == p->e && p->left!= NULL && p->right!= NULL)
{
Node* pmax = p->left;
while( pmax->right != NULL)
{
pmax = pmax->right;
}
p->e = pmax->e;
erase(p->e,p->left);
}
else //最終的刪除會在這裡執行
{
Node* pNew = p->left==NULL ? p->right : p->left;
delete p;
p = pNew;
mLength--;
}
if ( p!=NULL)
p->h = max( height(p->left),height(p->right)) + 1;
}
#endif

//main.cpp

#include <iostream>
#include "AVLTree.h"
using namespace std;

void main()
{
int Arr[9] = {6,2,8,4,10,0,12,16,14};
AVLTree<int> Tr(Arr,9);
Tr.traverse(Tr.getRoot());
Tr.traverseByLevel(Tr.getRoot());

Tr.erase(14,Tr.getRoot());
Tr.traverse(Tr.getRoot());
Tr.traverseByLevel(Tr.getRoot());
cout<<"Tree's length is:"<<Tr.getLength()<<endl;
Tr.clear();
cout<<"Tree's length is:"<<Tr.getLength()<<endl;

}

4 測試結果

C++ 設計新思維》 下��見 http://www.linuxidc.com/Linux/2014-07/104850.htm

C++ Primer Plus 第6版 中文版 清晰有書簽PDF+源代碼 http://www.linuxidc.com/Linux/2014-05/101227.htm

讀C++ Primer 之構造函數陷阱 http://www.linuxidc.com/Linux/2011-08/40176.htm

讀C++ Primer 之智能指針 http://www.linuxidc.com/Linux/2011-08/40177.htm

讀C++ Primer 之句柄類 http://www.linuxidc.com/Linux/2011-08/40175.htm

將C語言梳理一下,分布在以下10個章節中:

  1. Linux-C成長之路(一):Linux下C編程概要 http://www.linuxidc.com/Linux/2014-05/101242.htm
  2. Linux-C成長之路(二):基本數據類型 http://www.linuxidc.com/Linux/2014-05/101242p2.htm
  3. Linux-C成長之路(三):基本IO函數操作 http://www.linuxidc.com/Linux/2014-05/101242p3.htm
  4. Linux-C成長之路(四):運算符 http://www.linuxidc.com/Linux/2014-05/101242p4.htm
  5. Linux-C成長之路(五):控制流 http://www.linuxidc.com/Linux/2014-05/101242p5.htm
  6. Linux-C成長之路(六):函數要義 http://www.linuxidc.com/Linux/2014-05/101242p6.htm
  7. Linux-C成長之路(七):數組與指針 http://www.linuxidc.com/Linux/2014-05/101242p7.htm
  8. Linux-C成長之路(八):存儲類,動態內存 http://www.linuxidc.com/Linux/2014-05/101242p8.htm
  9. Linux-C成長之路(九):復合數據類型 http://www.linuxidc.com/Linux/2014-05/101242p9.htm
  10. Linux-C成長之路(十):其他高級議題

Copyright © Linux教程網 All Rights Reserved