平衡二叉树(AVL树)的查找,插入和删除
2015-11-15 22:41
507 查看
1,http://blog.csdn.net/zzuchengming/article/details/49824561这是我转载的一个讲解AVL树的文章,思路很好,内容也很清晰,这里基础知识不再赘述!
主要是因为树是一步步建立的,需要旋转的情况就四种,左左(意思是该节点的左子树比右子树高2,且该节点的左子树的左子树,比该节点的左子树的右子树要高),右右,左右,右左!
2,下边是我参考这篇文章写的代码,发现了其中的一个小错误!
4,增加一个问题:高度为n的平衡二叉树最少需要多少个节点?
设f(n)为高度为n的平衡二叉树最少含有的节点数,则:f(1) = 1;f(2) = 2; f(3) = 4;f(4) = 7;……
这些可以通过画图就能得到,但是当n很大时呢?其实有如下结论:f(n) = f(n-1) + f(n-2) +1,(n>=3)。这个递推结论如何得到的呢?
引导问题:求一棵二叉树的[b]节点数目:[/b]
假设一颗二叉树T,其左右子树分别为TL,TR。又假设T的节点数目为F(T),TL,TR的节点数目分别为F(TL),F(TR)。则显然:
F(T) = F(TL) + F(TR) + 1。
本文的问题:求高度为n的平衡二叉树最小需要多少节点:
同样假设T为高度为n的平衡二叉树,其需要最少的节点数目为F(n)。又假设TL,TR为T的左右子树,因此TL,TR也为平衡二叉树。假设F1,F2为TL,TR的最少节点数,则,F(n) = F1+F2 +1。那么F1,F2 到底等于多少呢?由于TL,TR与T一样是平衡二叉树,又由于我们知道T的最少节点数是F(n),其中n为T的高度,因此如果我们知道TL,TR的高度就可以知道F1,F2的值了。由平衡二叉树的定义可以知道,TL和TR的高度要么相同,要么相差1,而当TL与TR高度相同(即:都等于n-1)时,我们算出来的F(n)并不能保证最小,因此只有当TL与TR高度相差一(即:一个高度为n-1,一个高度为n-2)时,计算出来的F(n)才能最小。此时我们假设TL比TR高度要高1(即:TL高度为n-1,TR高度为n-2),则有:F1
= F(n-1),F2 = F(n-2)。因此得到结论:F(n) = F(n-1) + F(n -2 ) + 1!
这个地方推导思路稍微有点不清晰,应该都能明白,这个问题本来是很简单的问题,通过这个问题,我们可以看出递归的思想在树结构中的重要性,通过递归将问题抽象为多个规模更小的同类问题是解决这类问题的关键!
主要是因为树是一步步建立的,需要旋转的情况就四种,左左(意思是该节点的左子树比右子树高2,且该节点的左子树的左子树,比该节点的左子树的右子树要高),右右,左右,右左!
2,下边是我参考这篇文章写的代码,发现了其中的一个小错误!
#include "stdafx.h" #include <iostream> using std::cin; using std::cout; using std::endl; //先建立AVL树的结点结构 int count = 0;//记录旋转次数,双旋转算两次 typedef int Status; typedef struct AVLnode{ Status data; AVLnode* lchild; AVLnode* rchild; int hgt;//表示以该节点为根的树的高度,初始为0;因为我们定义空树高度为-1,一个结点为0; AVLnode() { lchild = NULL; rchild = NULL; hgt = 0; } }*AVLTree; //下边是要用到的函数的声明; void InsertToAVLTree(AVLTree &T, Status data);//插入函数 AVLnode* SearchAVLTree(AVLTree T, Status data);//查找函数 void DeleteAVLTree(AVLTree &T, Status data);//删除函数 int FindHeight(AVLnode *node);//求该结点的高度 int Max(int height1, int height2);//比较左右孩子的高度 void SingleRolateLeft(AVLnode *&k2);//单左旋转,即左左旋转,k2是被调整点 void SingleRolateRight(AVLnode *&k2);//单右旋转,即右右旋转,k2是被调整点 void DoubleRolateLR(AVLnode *&k4);//双向左右旋转 void DoubleRolateRL(AVLnode *&k4);//双向右左旋转 void InorderTraver(AVLTree T); //下边是用到的函数的定义; //插入函数,有引用T,必须初始化 void InsertToAVLTree(AVLTree &T, Status data) { if (T == NULL) { T = new AVLnode; T->data = data; return; } else { if (T->data < data) { InsertToAVLTree(T->rchild, data);//往T的右子树上插入 if (2 == FindHeight(T->rchild) - FindHeight(T->lchild)) { if (data < T->rchild->data)//满足右左的情况 { DoubleRolateRL(T); } else { SingleRolateRight(T); } } } else if (T->data > data) { InsertToAVLTree(T->lchild, data);//往T的左子树上插入 if (2 == FindHeight(T->lchild) - FindHeight(T->rchild)) { if (data > T->lchild->data) { DoubleRolateLR(T); } else { SingleRolateLeft(T); } } } else//相等不做什么 { } T->hgt = Max(FindHeight(T->lchild), FindHeight(T->rchild)) + 1; } } //单左旋转(左左)函数 void SingleRolateLeft(AVLnode *&k2) { count++; AVLnode *k1 = k2->lchild; k2->lchild = k1->rchild; k1->rchild = k2; cout << "第" << count << "次旋转!" << endl; cout << "被改子树旋转后根节点的右孩子是:" << k1->rchild->data << endl; k2->hgt = Max(FindHeight(k2->lchild), FindHeight(k2->rchild)) + 1; //k1->hgt = Max(FindHeight(k1->lchild), FindHeight(k1->rchild)) + 1;换成下边更好 k1->hgt = Max(FindHeight(k1->lchild), k2->hgt) + 1; //有一个问题就是旋转后,指向k2所在节点的指针应该改为指向k1所在节点的指针 k2 = k1;//旋转后k2要变!!!!!!!!!!!!!需要放到最后 //因为要先求得当前K2的高度! } //单右旋转(右右)函数 void SingleRolateRight(AVLnode *&k2) { count++; AVLnode *k1 = k2->rchild; k2->rchild = k1->lchild; k1->lchild = k2; cout << "第" << count << "次旋转!" << endl; cout << "被改子树旋转后根节点的右孩子是:" << k1->rchild->data << endl; k2->hgt = Max(FindHeight(k2->lchild), FindHeight(k2->rchild)) + 1; k1->hgt = Max(FindHeight(k1->rchild), k2->hgt) + 1; k2 = k1;//旋转后k2要变!!!!!!!!!!!!!要放到最后 } //双旋转(左右)函数 void DoubleRolateLR(AVLnode *&k4)//先右单旋再左单旋 { AVLnode *k3 = k4->lchild; SingleRolateRight(k3); SingleRolateLeft(k4); } //双旋转(右左)函数 void DoubleRolateRL(AVLnode *&k4)//先左单旋再右单旋 { AVLnode *k3 = k4->rchild; SingleRolateLeft(k3); SingleRolateRight(k4); } AVLnode* SearchAVLTree(AVLTree T, Status data) { if (T == NULL) { cout << "查找点不在树中!" << endl; return NULL; } else { if (T->data < data) { return SearchAVLTree(T->rchild, data); } else if (T->data > data) { return SearchAVLTree(T->lchild, data); } else { cout << "找到了,值为" << T->data << endl; return T; } } } void DeleteAVLTree(AVLTree &T, Status data) { if (T == NULL) { cout << "该元素不在AVL树中!" << endl; return; } else { if (T->data < data) { DeleteAVLTree(T->rchild, data); if (2 == FindHeight(T->lchild) - FindHeight(T->rchild)) { if (T->lchild->rchild != NULL && FindHeight(T->lchild->rchild) > FindHeight(T->lchild->lchild)) { //这里只有T->lchild->rchild不为空时,才有可能会是左右的情况。 DoubleRolateLR(T); } else { SingleRolateLeft(T); } } } else if (T->data > data) { DeleteAVLTree(T->lchild, data); if (2 == FindHeight(T->rchild) - FindHeight(T->lchild)) { if (T->rchild->lchild != NULL && FindHeight(T->rchild->lchild) > FindHeight(T->rchild->rchild)) { DoubleRolateRL(T); } else { SingleRolateRight(T); } } } else//相等的情况,若有两个孩子则找到它的右子树中最小的作为替换点 { if (T->lchild && T->rchild) { AVLnode *r = T->rchild; while (r->lchild) { r = r->lchild; } //<span style="font-size:18px;color:#000099;">找到替换点了,替换,下边删除的点变成了r->data了</span> T->data = r->data;//替换后,要从T开始往下接着删除操作 DeleteAVLTree(T->rchild, r->data); if (2 == FindHeight(T->lchild) - FindHeight(T->rchild)) { if (T->lchild->rchild != NULL && FindHeight(T->lchild->rchild) > FindHeight(T->lchild->lchild)) { DoubleRolateLR(T); } else { SingleRolateLeft(T); } } } else//只有一个孩子或者没有孩子 { if (!T->lchild)//若左孩子为空 { AVLnode* t = T; T = T->rchild;//直接接右孩子,这里不需要再判断是否有等于2的情况了
delete t; //T被删了,要有等于2的情况也是T的父亲,而对于它父亲的判断 } //在前面已经考虑过了! else { AVLnode* t = T; T = T->lchild; delete t; } } } //从宏观上看,在T上删除元素后,T的高度是会变的 if (T == NULL) { return; } else { T->hgt = Max(FindHeight(T->lchild), FindHeight(T->rchild)) + 1; return; } } } //找结点高度函数 int FindHeight(AVLnode *node)//这个函数我认为只能用在AVL树是通过上边插入函数得来的, { //不能是普通的递归建树,因为这个高度是在建树过程中一步步更新得来的 if (!node) { return -1; } else { return node->hgt; } } //比较大小函数 int Max(int height1, int height2) { return height1 > height2 ? height1:height2; } //中序遍历函数 void InorderTraver(AVLTree T) { if (T == NULL) { return; } else { InorderTraver(T->lchild); cout << T->data << ' '; InorderTraver(T->rchild); } } int _tmain(int argc, _TCHAR* argv[]) { AVLTree T1 = NULL; int a1[] = {11,7,3,9,18,15,14,16,26}; int a2[] = {3,7,9,11,14,15,16,18,26}; for (int i = 0; i < 9; i++) { InsertToAVLTree(T1, a1[i]); } InorderTraver(T1); cout << endl; cout << "查找元素16" << endl; SearchAVLTree(T1, 16); cout << "删除元素16" << endl; DeleteAVLTree(T1, 16); cout << "删后中序遍历为:" << endl; InorderTraver(T1); cout << endl; cout << "删除16后再查找16" << endl; SearchAVLTree(T1, 16); cout << endl; cout << "再删除14" << endl; DeleteAVLTree(T1, 14);//应该是有1次旋转 //cout << "不同的输入顺序中序遍历结果:" << endl; ////但是所需要的旋转次数不同 //AVLTree T2 = NULL; //for (int i = 0; i < 9; i++) //{ // InsertToAVLTree(T2, a2[i]); //} //InorderTraver(T2); system("pause"); return 0; }3,显示结果为:
4,增加一个问题:高度为n的平衡二叉树最少需要多少个节点?
设f(n)为高度为n的平衡二叉树最少含有的节点数,则:f(1) = 1;f(2) = 2; f(3) = 4;f(4) = 7;……
这些可以通过画图就能得到,但是当n很大时呢?其实有如下结论:f(n) = f(n-1) + f(n-2) +1,(n>=3)。这个递推结论如何得到的呢?
引导问题:求一棵二叉树的[b]节点数目:[/b]
假设一颗二叉树T,其左右子树分别为TL,TR。又假设T的节点数目为F(T),TL,TR的节点数目分别为F(TL),F(TR)。则显然:
F(T) = F(TL) + F(TR) + 1。
本文的问题:求高度为n的平衡二叉树最小需要多少节点:
同样假设T为高度为n的平衡二叉树,其需要最少的节点数目为F(n)。又假设TL,TR为T的左右子树,因此TL,TR也为平衡二叉树。假设F1,F2为TL,TR的最少节点数,则,F(n) = F1+F2 +1。那么F1,F2 到底等于多少呢?由于TL,TR与T一样是平衡二叉树,又由于我们知道T的最少节点数是F(n),其中n为T的高度,因此如果我们知道TL,TR的高度就可以知道F1,F2的值了。由平衡二叉树的定义可以知道,TL和TR的高度要么相同,要么相差1,而当TL与TR高度相同(即:都等于n-1)时,我们算出来的F(n)并不能保证最小,因此只有当TL与TR高度相差一(即:一个高度为n-1,一个高度为n-2)时,计算出来的F(n)才能最小。此时我们假设TL比TR高度要高1(即:TL高度为n-1,TR高度为n-2),则有:F1
= F(n-1),F2 = F(n-2)。因此得到结论:F(n) = F(n-1) + F(n -2 ) + 1!
这个地方推导思路稍微有点不清晰,应该都能明白,这个问题本来是很简单的问题,通过这个问题,我们可以看出递归的思想在树结构中的重要性,通过递归将问题抽象为多个规模更小的同类问题是解决这类问题的关键!
相关文章推荐
- weblogic部署缓存问题
- c++单元测试指南:使用google test
- 免费的编程中文书籍索引
- 分享一个自己做的图片轮播的插件
- 装饰器设计模式
- acm的一些常见提示及原因
- IOS KVO笔记
- 从粗放式到精益化编程
- 使用ScrapBook插件下载Google Books的方法
- Android 手机小闹钟
- yum安装PHP7
- PhoneGap创建的安卓WebApp项目如何去除全屏,显示手机顶部状态栏
- linux下设置线程优先级 http://blog.csdn.net/typhoonzb/article/details/4544278
- Unity中用到的C#补充(五)- 简单的本地存储
- Spring依赖原理
- 前端知识体系
- 归并排序
- 用socket方式连接Nginx优化php-fpm性能
- miner大后端配置步骤
- 【区域赛总结】2015ACM-ICPC北京区域赛