二叉查找树和AVL树的基本操作(AVL树就是一颗特殊的二叉查找树。)
2012-04-10 16:40
155 查看
二叉查找树的特点是:
1 对于每一棵子树而言,其根节点大于所有左子树的值,小于所有右子树上的值(因为是用于查找,所以每个节点中包含的都是关键值,因此不应有重复的)。
2 其本身的特点决定了二叉排序树生成的特点,生成代码也是简单的。
3 删除的时候分为只有左子树/右子树,和既有左子树又有右子树两种情况来进行解决。根据自身特点可以最终归结为删除只有左右子树的节点。
4 由于自身特点,中序遍历一棵二叉排序树得到的结果就是一个递增序列。
AVL数的特点是:
对于二叉排序树的1和4,AVL数也是具备的,但是由于AVL树本身具备更强的限制条件,要求所有左子树和右子树高度差值的绝对值不能超过1,在插入和删除的时候不仅要保证其有序性,还要保证它的平衡性。所以它的插入删除有着更为复杂的操作。
1 一个很重要的结论就是,在每一次插入或者删除一个节点的时候,只有在插入或者删除点(删除点是实际的要删除的节点,因为开始的时候会将删除有两个子树的节点转换为删除至多有一个子树的节点)到根的路径上的节点才有可能发生平衡变化,因为只有这些节点的子树发生了变化。
2所谓最小不平衡子树指离插入节点最近且以平衡因子的绝对值大于1的节点作为根的子树。其中在插入节点时候关键的一点就是找到最小不平衡子树的根节点。
3 AVL树的插入可以分为四种情况:向最小不平衡子树根节点的左孩子的左子树上插入造成,定义为LL型;向根节点的左孩子的右子树上插入造成,定义为LR型;向根节点的右孩子的右子树上插入造成,定义为RR型;向根节点的右孩子的左子树插入造成,定义为RL型。其中LL和RR是基本类型,LR类型可以用处理根节点左孩子的RR类型再处理根节点的LL类型来等价,RL类型可以用处理根节点右孩子的LL类型再处理根节点的RR类型来等价。其特点就是处理完这个最小不平衡子树后不用再回溯,因为之上的肯定都已经是平衡树了。
4 AVL树的删除相对于插入就比较复杂了。设P为要删除节点的父节点,可以分为三种情况:P平衡因子原为0,删除使得P的某棵子树边矮,最容易处理;从P的较高的子树上删除一个节点,使得较高的子树变矮,这个时候不仅会影响P的平衡,很可能还会影响P的父节点的平衡,所以要回溯到根节点,直至遇到某个节点的平衡因子原来为0就停止回溯(当然这里还会包含平衡的调整);从P的较矮的子树上删除一个节点,使得该子树变得更矮,这个时候设这棵较高的子树的根节点为Q,此时又分为Q的平衡因子为0,Q的平衡因子和原P的平衡因子同号和Q的平衡因子和原P的平衡因子异号三种情况进行处理。
说明:在这里求取每个节点的平衡因子都是通过一个height函数,来计算特定节点的左右子树的差距确定的,节点结构中没有记录平衡因子,记录的是以这个节点为根的子树的高度。在插入的时候是通过计算节点的左子树和右子树的差值来查看是否不平衡需要调整;在删除的时候首先确定是在哪个子树上删除,然后计算删除节点所在子树的根节点的平衡因子,之后再细分从而实现节点删除之后造成的不平衡的调整。
其次,关于遍历、查找、删除就是树或者二分查找树的通用算法了。
高度为n的平衡二叉树的最少节点数,设为F(n),则有F(n)=F(n-1)+F(n-2)+1;其中F(0)=0,F(1)=1。可以得到F(n)=F(n+2)-1。F为斐波那契额级数。其实这个也可以根据递归的想法总结得到,证明则使用数学归纳法。
属于斐波那契数列的应用(后一项属于前两项之和,第零项为0,第一项为1)。
AVL的删除某个key值的另外一个思路:
从AVL树中根开始,将和要删除key值不同的全部重新插入一遍,遇到key值则不插入,将key值所在节点的左子树和右子树的元素递归插入即可。这样每次删除一个元素的过程转换为了重新构建一棵AVL树的过程,代价比较大!
同样按照某个值分裂AVL树的过程可以转化为重新构建两颗AVL树的过程;及合并AVL树都可以转换为将一棵AVL树中的元素插入到另外一棵AVL树中的过程。
实现代码:
测试代码
1 对于每一棵子树而言,其根节点大于所有左子树的值,小于所有右子树上的值(因为是用于查找,所以每个节点中包含的都是关键值,因此不应有重复的)。
2 其本身的特点决定了二叉排序树生成的特点,生成代码也是简单的。
3 删除的时候分为只有左子树/右子树,和既有左子树又有右子树两种情况来进行解决。根据自身特点可以最终归结为删除只有左右子树的节点。
4 由于自身特点,中序遍历一棵二叉排序树得到的结果就是一个递增序列。
AVL数的特点是:
对于二叉排序树的1和4,AVL数也是具备的,但是由于AVL树本身具备更强的限制条件,要求所有左子树和右子树高度差值的绝对值不能超过1,在插入和删除的时候不仅要保证其有序性,还要保证它的平衡性。所以它的插入删除有着更为复杂的操作。
1 一个很重要的结论就是,在每一次插入或者删除一个节点的时候,只有在插入或者删除点(删除点是实际的要删除的节点,因为开始的时候会将删除有两个子树的节点转换为删除至多有一个子树的节点)到根的路径上的节点才有可能发生平衡变化,因为只有这些节点的子树发生了变化。
2所谓最小不平衡子树指离插入节点最近且以平衡因子的绝对值大于1的节点作为根的子树。其中在插入节点时候关键的一点就是找到最小不平衡子树的根节点。
3 AVL树的插入可以分为四种情况:向最小不平衡子树根节点的左孩子的左子树上插入造成,定义为LL型;向根节点的左孩子的右子树上插入造成,定义为LR型;向根节点的右孩子的右子树上插入造成,定义为RR型;向根节点的右孩子的左子树插入造成,定义为RL型。其中LL和RR是基本类型,LR类型可以用处理根节点左孩子的RR类型再处理根节点的LL类型来等价,RL类型可以用处理根节点右孩子的LL类型再处理根节点的RR类型来等价。其特点就是处理完这个最小不平衡子树后不用再回溯,因为之上的肯定都已经是平衡树了。
4 AVL树的删除相对于插入就比较复杂了。设P为要删除节点的父节点,可以分为三种情况:P平衡因子原为0,删除使得P的某棵子树边矮,最容易处理;从P的较高的子树上删除一个节点,使得较高的子树变矮,这个时候不仅会影响P的平衡,很可能还会影响P的父节点的平衡,所以要回溯到根节点,直至遇到某个节点的平衡因子原来为0就停止回溯(当然这里还会包含平衡的调整);从P的较矮的子树上删除一个节点,使得该子树变得更矮,这个时候设这棵较高的子树的根节点为Q,此时又分为Q的平衡因子为0,Q的平衡因子和原P的平衡因子同号和Q的平衡因子和原P的平衡因子异号三种情况进行处理。
说明:在这里求取每个节点的平衡因子都是通过一个height函数,来计算特定节点的左右子树的差距确定的,节点结构中没有记录平衡因子,记录的是以这个节点为根的子树的高度。在插入的时候是通过计算节点的左子树和右子树的差值来查看是否不平衡需要调整;在删除的时候首先确定是在哪个子树上删除,然后计算删除节点所在子树的根节点的平衡因子,之后再细分从而实现节点删除之后造成的不平衡的调整。
其次,关于遍历、查找、删除就是树或者二分查找树的通用算法了。
高度为n的平衡二叉树的最少节点数,设为F(n),则有F(n)=F(n-1)+F(n-2)+1;其中F(0)=0,F(1)=1。可以得到F(n)=F(n+2)-1。F为斐波那契额级数。其实这个也可以根据递归的想法总结得到,证明则使用数学归纳法。
属于斐波那契数列的应用(后一项属于前两项之和,第零项为0,第一项为1)。
AVL的删除某个key值的另外一个思路:
从AVL树中根开始,将和要删除key值不同的全部重新插入一遍,遇到key值则不插入,将key值所在节点的左子树和右子树的元素递归插入即可。这样每次删除一个元素的过程转换为了重新构建一棵AVL树的过程,代价比较大!
同样按照某个值分裂AVL树的过程可以转化为重新构建两颗AVL树的过程;及合并AVL树都可以转换为将一棵AVL树中的元素插入到另外一棵AVL树中的过程。
实现代码:
#include <vector> using namespace std; template <class Type> class AvlTree{ struct AvlNode { Type data; AvlNode *left; AvlNode *right; int height; AvlNode(const Type& element, AvlNode *lt, AvlNode *rt, int h=0) :data(element),left(lt),right(rt),height(h){} }; AvlNode *root; public: vector<Type> vType; //存储遍历结果 typename vector<Type>::iterator iType; //这里需要使用typeneme说明一个嵌套类型 vector<AvlNode *> vNode; typename vector<AvlNode*>::iterator iNode; AvlTree(AvlNode *t=NULL) {root = t;} ~AvlTree(){makeEmpty(root);} bool find(const Type& x) const; void insert(const Type& x){insert(x,root);} void remove(const Type& x){remove(x,root);} void traverse() { //递归中序遍历 vType.clear(); //每次遍历之前清空上一次遍历结果! vNode.clear(); traverse(root); } private: void insert(const Type&x, AvlNode *&t); //t的意义是表示一个int*指针类型的别名 bool remove(const Type&x, AvlNode *&t); void traverse(AvlNode *&t); void makeEmpty(AvlNode *&t); int height(AvlNode *t) const{return t==NULL ? -1 : t->height;} void LL(AvlNode *&t); void LR(AvlNode *&t); void RL(AvlNode *&t); void RR(AvlNode *&t); int max(int a, int b){return (a>b)?a:b;} }; template <class Type> bool AvlTree<Type>::find(const Type &x) const { AvlNode *t=root; while (t!=NULL && t->data!=x) { if(t->data>x) t=t->left; else t=t->right; } if(t==NULL) return false; else return true; } template <class Type> void AvlTree<Type>::traverse(AvlNode *&t) { if (t!=NULL) { traverse(t->left); vType.push_back(t->data); vNode.push_back(t); traverse(t->right); } } template <class Type> void AvlTree<Type>::makeEmpty(AvlNode *&t) { for (iNode=vNode.begin();iNode!=vNode.end();iNode++) { delete (*iNode); } } template <class Type> void AvlTree<Type>::insert(const Type&x, AvlNode *&t) { if(t==NULL) t=new AvlNode(x,NULL,NULL); else if (x<t->data) { insert(x,t->left); if (height(t->left)-height(t->right)==2) { if(x<t->left->data) LL(t); else LR(t); } } else if (t->data<x) { insert(x,t->right); if (height(t->right)-height(t->left)==2) { if(t->right->data<x) RR(t); else RL(t); } } t->height=max(height(t->left),height(t->right))+1; } template <class Type> void AvlTree<Type>::LL(AvlNode *&t) { AvlNode *t1=t->left; t->left=t1->right; t1->right=t; t->height=max(height(t->left),height(t->right))+1; t1->height=max(height(t1->left),height(t))+1; t=t1; } template <class Type> void AvlTree<Type>::RR(AvlNode *&t) { AvlNode *t1=t->right; t->right=t1->left; t1->left=t; t->height=max(height(t->left),height(t->right))+1; t1->height=max(height(t1->right),height(t)); t=t1; } template<class Type> void AvlTree<Type>::LR(AvlNode *&t) { RR(t->left); LL(t); } template<class Type> void AvlTree<Type>::RL(AvlNode *&t) { LL(t->right); RR(t); } template<class Type> bool AvlTree<Type>::remove(const Type&x, AvlNode *&t) { bool stop=false; int subTree; //1表示删除发生在左子树,2表示删除发生在右子树 if(t==NULL) return true; if (x<t->data) { stop=remove(x,t->left); subTree=1; } else if (t->data<x) { stop=remove(x,t->right); subTree=2; } else if (t->left!=NULL && t->right!=NULL) { AvlNode *tmp=t->right; while (tmp->left!=NULL) tmp=tmp->left; t->data=tmp->data; stop=remove(t->data,t->right); subTree=2; } else { AvlNode *oldNode=t; t=(t->left!=NULL)?t->left:t->right; delete oldNode; return false; } if(stop) return true; int bf; //删除前的平衡因子 switch (subTree) { case 1: bf=height(t->left)-height(t->right)+1; if(bf==0) return true; if(bf==1) return false; if (bf==-1) { int bfr=height(t->right->left)-height(t->right->right); switch (bfr) { case 0: RR(t); return true; break; case -1: RR(t); return false; break; default: RL(t); return false; } } break; case 2: bf=height(t->left)-height(t->right)-1; if(bf==0) return true; if(bf==-1) return false; if(bf==1) { int bfl=height(t->right->left)-height(t->right->right); switch (bfl) { case 0: LL(t); return true; break; case 1: LL(t); return false; break; default: LR(t); return false; } } break; } }
测试代码
#include <iostream> #include <vector> #include "main.h" using namespace std; int main() { //构建一棵AVL树 int a[] = {10,8,6,21,87,56,4,0,11,3,22,7,5,34,1,2,9}; AvlTree<int> tree; for(int i=0; i<17; i++) tree.insert(a[i]); cout << endl; //查找是否在AVL树中 int val1=22,val2=100; if(tree.find(val1)) cout << "find val1 ok!" << endl; else cout << "NOT find val1" << endl; if(tree.find(val2)) cout << "find val2 ok!" << endl; else cout << "NOT find val2" << endl; tree.traverse(); cout << "NOW the Tree Size is:" << tree.vType.size() << endl; for(tree.iType=tree.vType.begin();tree.iType!=tree.vType.end();tree.iType++) { cout << " " << *(tree.iType); } cout << endl << "++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++" <<endl; //移除之后再查找 tree.remove(val1); if(tree.find(val1)) cout << "find val1 ok!" << endl; else cout << "NOT find val1" << endl; tree.traverse(); cout << "NOW the Tree Size is:" << tree.vType.size() << endl; for(tree.iType=tree.vType.begin();tree.iType!=tree.vType.end();tree.iType++) { cout << " " << *(tree.iType); } cout << endl << "++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++" <<endl; return 0; }
相关文章推荐
- AVL树的基本操作 HDU_4006 AVL树解法
- Avl树的基本操作(c语言实现)
- 二叉树的基本操作(三)——AVL树的性质和插入操作
- 二叉排序树(二叉查找树)的基本操作
- 二叉查找树基本操作
- matlab基本操作 关键字 特殊变量 常用命令 数据结构
- Python语句中基本的规则与特殊字符简单操作实例
- 设计一个特殊的栈,在实现栈的基本功能的基础上,再实现返回栈中最小元素的操作
- AVL树基本操作
- 标题:AVL树的基本操作例程(1)
- Oracle基本操作八:PL/SQL特殊数据类型%type %rowtype VARRAY TABLE RECORD
- 二叉查找树(binary search tree)上的基本操作
- 二叉树的性质以及二叉查找树的基本操作
- 平衡二叉树(AVL树)的基本操作
- AVL树的基本操作之插入(递归与非递归编码)(2)
- 最基本的就是在笔记本上进行操作,装置的必须是正版Windows
- Python语句中基本的规则与特殊字符简单操作实例
- 二叉查找树的基本操作(建立,插入,删除,遍历)
- 平衡二叉树(AVL树)的基本操作(附有示意图)
- AVL树的定义和基本操作