数据结构::浅析平衡二叉树
来源:互联网 发布:mac怎么下载歌 编辑:程序博客网 时间:2024/06/16 08:58
【为什么我们要引入平衡二叉树?】
在学习完搜索二叉树后,我们知道搜索二叉树退化了之后,它的样子和单链表很像(如下图),在这种情况下,我们如果进行搜索的话,它的时间复杂度就大大降低了,此时的时间复杂度是O(N)。
因此我们引入了平衡二叉树,它解决了搜索二叉树效率低的缺点
【平衡二叉树(AVL树)】:
1、平衡二叉树的概念:
(首先它是搜索二叉树)要么是空树,要么是具有以下性质的二叉树:
1)左右子树都是平衡二叉树
2)左右子树的高度之差不超过1
3)每个节点都有一个平衡因子,平衡因子的取值范围只能是-1,0,1.
2、平衡因子:
平衡二叉树中左右子树的高度或者说深度之差,我们可以知道,平衡因子的取值为:-1,0,1.
说明:我们用平衡因子的大小来判断是不是平衡二叉树
3、节点的定义(结构):
说明:关于在结构中,多了一个关键值,这个关键值的作用我后面会讲到。
template<class K,class V>struct AVLTreeNode{K _key; //关键字V _value; //关键值int _bf; //平衡因子AVLTreeNode<K,V>* _left;AVLTreeNode<K,V>* _right;AVLTreeNode<K,V>* _parent;AVLTreeNode(const K& key,V& value):_left(NULL),_right(NULL),_parent(NULL),_key(key),_value(value),_bf(0){}};
4、平衡二叉树的查找
说明:在查找的时候和搜索二叉树是一样的
Node* Find(const K& key){if(_root == NULL)return NULL;Node* cur = _root;while(cur){if(cur->_key > key){cur = cur->_left;}else if(cur->_key < key){cur = cur->_right;}else{return cur;}}return NULL;}
5、平衡二叉树的插入(重点)
我们来看下插入的步骤:
1)首先我们先找到要插入的点
2)找到之后,插入要插入的值
3)此时插入之后,就要进行平衡因子的调节。
** 插入之后,父节点的平衡因子要分的情况:
**旋转的时候又要进行细分,下面我用图解来进行说明:
代码的实现:
**插入的代码
bool Insert(const K& key,V& value){if(_root == NULL){_root = new Node(key,value);return true;}Node* cur = _root;Node* parent = NULL;//1、先找要插入的点while(cur){if(cur->_key > key){parent = cur;cur = cur->_left;}else if(cur->_key < key){parent = cur;cur = cur->_right;}else{return false;}}//2、此时插入要插入的值cur = new Node(key,value);if(parent->_key > key){parent->_left = cur;//parent = cur->_parent;cur->_parent = parent;}else{parent->_right = cur;//parent = cur->_parent;cur->_parent = parent;}//3、插入之后就要进行调整,因为插入之后不知道它是否还是AVL树//父节点的平衡因子分情况:(更新后)//1)为0,说明之前是1,无需向上更新//2)为1/-1,说明之前是0,往上更新//3)为2/-2,不满足,无需更新,进行旋转while(parent){if(parent->_left == cur){parent->_bf -= 1;}else{parent->_bf += 1;}//正式进行平衡因子的调节if(parent->_bf == 0){break;}else if(parent->_bf == 1 || parent->_bf == -1){cur = parent;parent = cur->_parent;}//进行旋转的时候又要进行分情况else if(parent->_bf == 2 || parent->_bf == -2){//四种情况:if(parent->_bf == 2){//左单旋if(cur->_bf == 1){RotateL(parent);}//右左单旋//说明:这里要分情况,插入点不同,平衡因子的改变就不同else //cur->bf == -1{RotateRL(parent);}}/*if(parent->_bf == -2)*/else{//右单旋if(cur->_bf == -1){RotateR(parent);}//左右单旋//说明:同理,插入点不同,平衡因子的改变就不同else{RotateLR(parent);}}break;}else{cout<<"平衡因子异常!"<<endl;assert(false);}}return true;}
**四个旋转的代码实现:
//左单旋void RotateL(Node* parent){Node* subR = parent->_right;Node* subRL = subR->_left;parent->_right = subRL;if(subRL)subRL->_parent = parent;Node* ppNode = parent->_parent;subR->_left = parent;parent->_parent = subR;if(ppNode == NULL){_root = subR;subR->_parent = NULL;}else{if(ppNode->_left == parent)ppNode->_left = subR;elseppNode->_right = subR;subR->_parent = ppNode;}parent->_bf = subR->_bf = 0;}//右单旋void RotateR(Node* parent){Node* subL = parent->_left;Node* subLR = subL->_right;parent->_left = subLR;if(subLR)subLR->_parent = parent;Node* ppNode = parent->_parent;subL->_right = parent;parent->_parent = subL;if(ppNode == NULL){_root = subL;subL->_parent = NULL;}else{if(ppNode->_left == parent)ppNode->_left = subL;elseppNode->_right = subL;subL->_parent = ppNode;}parent->_bf = subL->_bf = 0;}/*//左右旋转void RotateLR(Node* parent){Node* cur = parent->_left;RotateL(cur);RotateR(cur);}//右左旋转void RotateRL(Node* parent){Node* cur = parent->_right;RotateR(cur);RotateL(cur);}*//***************经过细分情况的双旋*****************///左右旋void RotateLR(Node* parent){Node* subL = parent->_left;Node* subLR = subL->_right;int bf = subLR->_bf;RotateL(parent->_left);RotateR(parent);if(bf == 1){subL->_bf = -1;parent->_bf = 0;}else if(bf == -1){subL->_bf = 0;parent->_bf = 1;}else {subL->_bf = parent->_bf = 0;}subLR->_bf = 0;}//右左旋void RotateRL(Node* parent){Node* subR = parent->_right;Node* subRL = subR->_left;int bf = subRL->_bf;RotateR(parent->_right);RotateL(parent);if(bf == 1){subR->_bf = 0;parent->_bf = -1;}else if(bf == -1){subR->_bf = 1;parent->_bf = 0;}else{subR->_bf = parent->_bf = 0;}subRL->_bf = 0;}
6、判断一棵树是不是平衡二叉树
说明:利用平衡因子进行衡量
1)//思路分析:利用高度看平衡因子是否是-1,0,1//采用这种方法时间复杂度是O(N*N)bool IsBalance(){return _IsBalance(_root);}bool _IsBalance(Node* root){//当是空树的时候也是AVL树if(root == NULL)return true;int leftdepth = _Depth(root->_left);int rightdepth = _Depth(root->_right);if((rightdepth-leftdepth) != root->_bf){cout<<"平衡因子异常:"<<root->_key<<endl;}return abs(rightdepth-leftdepth)<=1&& _IsBalance(root->_left)&& _IsBalance(root->_right);}2)//经过优化后判断是否是平衡二叉树//思路分析:只遍历一次,每次进行递归回去的返回值// 是将上一次的高度带回//时间复杂度是O(N*N)bool IsBalanceOP(){size_t depth = 0;return _IsBalanceOP(_root,depth);}bool _IsBalanceOP(Node* root,size_t depth){if(root == NULL){depth = 0;return true;}size_t leftdepth = 0;size_t rightdepth = 0;//先进行判断此节点的左右子树是否是平衡二叉树if(_IsBalanceOP(root->_left,leftdepth)&& _IsBalanceOP(root->_right,rightdepth)){//如果是的话。那么就再来进行判断此节点是平衡二叉树//并且将左右节点中高度带回depth = leftdepth > rightdepth? leftdepth+1:rightdepth+1;return abs(leftdepth-rightdepth) < 2;}return false;}
7、附加:
1)求平衡二叉树的高度
size_t Depth(){return _Depth(_root);}size_t _Depth(Node* &root){if(root == NULL)return 0;size_t leftD = _Depth(root->_left);size_t rightD = _Depth(root->_right);return leftD>rightD ? leftD+1:rightD+1;}
2)中序遍历(由搜索二叉树的性质可知,中序遍历后是按从小到大进行排好序的数)
void InOrder(){ _InOrder(_root);}void _InOrder(Node* root){if(root == NULL)return;_InOrder(root->_left);cout<<root->_key<<" ";_InOrder(root->_right);}
8、关于测试用例
说明:这里我给出两组测试用例,在我上面写双旋的时候写了两个版本,在第一个测试用例中使用是没有问题的,但是在第二个测试用例中,如果使用第一个版本就会出现问题,在双旋的树中,再进行插入的时候是要进行分情况讨论的,于是上面我也给出了第二版本,更完善。
#include"AVLTree.h"void Test(){AVLTree<int,int> t1;int a[] = {16,3,7,11,9,26,18,14,15};for(int i = 0; i<(sizeof(a)/sizeof(a[0])); i++){t1.Insert(a[i],i);}t1.InOrder();cout<<"IsBalance: "<<t1.IsBalance()<<endl;//AVLTree<int,int> t2;//int a[] = {4, 2, 6, 1, 3, 5, 15, 7, 16, 14};//for(int i = 0; i<(sizeof(a)/sizeof(a[0])); i++)//{//t2.Insert(a[i],i);////cout<<"IsBalance: "<<t2.IsBalance()<<endl;//} //t2.InOrder();//cout<<"IsBalance: "<<t2.IsBalance()<<endl;}int main(){Test();return 0;}
9、完整代码
1)主要的功能实现代码
#include<iostream>#include<assert.h>#include<math.h>using namespace std;template<class K,class V>struct AVLTreeNode{K _key; //关键字V _value; //关键值int _bf; //平衡因子AVLTreeNode<K,V>* _left;AVLTreeNode<K,V>* _right;AVLTreeNode<K,V>* _parent;AVLTreeNode(const K& key,V& value):_left(NULL),_right(NULL),_parent(NULL),_key(key),_value(value),_bf(0){}};template<class K,class V>class AVLTree{public:typedef AVLTreeNode<K,V> Node;public://构造函数AVLTree():_root(NULL){}//查找函数Node* Find(const K& key){if(_root == NULL)return NULL;Node* cur = _root;while(cur){if(cur->_key > key){cur = cur->_left;}else if(cur->_key < key){cur = cur->_right;}else{return cur;}}return NULL;}//插入函数bool Insert(const K& key,V& value){if(_root == NULL){_root = new Node(key,value);return true;}Node* cur = _root;Node* parent = NULL;//1、先找要插入的点while(cur){if(cur->_key > key){parent = cur;cur = cur->_left;}else if(cur->_key < key){parent = cur;cur = cur->_right;}else{return false;}}//2、此时插入要插入的值cur = new Node(key,value);if(parent->_key > key){parent->_left = cur;//parent = cur->_parent;cur->_parent = parent;}else{parent->_right = cur;//parent = cur->_parent;cur->_parent = parent;}//3、插入之后就要进行调整,因为插入之后不知道它是否还是AVL树//父节点的平衡因子分情况:(更新后)//1)为0,说明之前是1,无需向上更新//2)为1/-1,说明之前是0,往上更新//3)为2/-2,不满足,无需更新,进行旋转while(parent){if(parent->_left == cur){parent->_bf -= 1;}else{parent->_bf += 1;}//正式进行平衡因子的调节if(parent->_bf == 0){break;}else if(parent->_bf == 1 || parent->_bf == -1){cur = parent;parent = cur->_parent;}//进行旋转的时候又要进行分情况else if(parent->_bf == 2 || parent->_bf == -2){//四种情况:if(parent->_bf == 2){//左单旋if(cur->_bf == 1){RotateL(parent);}//右左单旋//说明:这里要分情况,插入点不同,平衡因子的改变就不同else //cur->bf == -1{RotateRL(parent);}}/*if(parent->_bf == -2)*/else{//右单旋if(cur->_bf == -1){RotateR(parent);}//左右单旋//说明:同理,插入点不同,平衡因子的改变就不同else{RotateLR(parent);}}break;}else{cout<<"平衡因子异常!"<<endl;assert(false);}}return true;//在循环外面}//判断一颗二叉树是否是AVL树//思路分析:利用高度看平衡因子是否是-1,0,1//采用这种方法时间复杂度是O(N*N)bool IsBalance(){return _IsBalance(_root);}bool _IsBalance(Node* root){//当是空树的时候也是AVL树if(root == NULL)return true;int leftdepth = _Depth(root->_left);int rightdepth = _Depth(root->_right);if((rightdepth-leftdepth) != root->_bf){cout<<"平衡因子异常:"<<root->_key<<endl;}return abs(rightdepth-leftdepth)<=1&& _IsBalance(root->_left)&& _IsBalance(root->_right);}//经过优化后判断是否是平衡二叉树//思路分析:只遍历一次,每次进行递归回去的返回值// 是将上一次的高度带回//时间复杂度是O(N*N)bool IsBalanceOP(){size_t depth = 0;return _IsBalanceOP(_root,depth);}bool _IsBalanceOP(Node* root,size_t depth){if(root == NULL){depth = 0;return true;}size_t leftdepth = 0;size_t rightdepth = 0;//先进行判断此节点的左右子树是否是平衡二叉树if(_IsBalanceOP(root->_left,leftdepth)&& _IsBalanceOP(root->_right,rightdepth)){//如果是的话。那么就再来进行判断此节点是平衡二叉树//并且将左右节点中高度带回depth = leftdepth > rightdepth? leftdepth+1:rightdepth+1;return abs(leftdepth-rightdepth) < 2;}return false;}//中序遍历void InOrder(){ _InOrder(_root);}void _InOrder(Node* root){if(root == NULL)return;_InOrder(root->_left);cout<<root->_key<<" ";_InOrder(root->_right);}protected://左单旋void RotateL(Node* parent){Node* subR = parent->_right;Node* subRL = subR->_left;parent->_right = subRL;if(subRL)subRL->_parent = parent;Node* ppNode = parent->_parent;subR->_left = parent;parent->_parent = subR;if(ppNode == NULL){_root = subR;subR->_parent = NULL;}else{if(ppNode->_left == parent)ppNode->_left = subR;elseppNode->_right = subR;subR->_parent = ppNode;}parent->_bf = subR->_bf = 0;}//右单旋void RotateR(Node* parent){Node* subL = parent->_left;Node* subLR = subL->_right;parent->_left = subLR;if(subLR)subLR->_parent = parent;Node* ppNode = parent->_parent;subL->_right = parent;parent->_parent = subL;if(ppNode == NULL){_root = subL;subL->_parent = NULL;}else{if(ppNode->_left == parent)ppNode->_left = subL;elseppNode->_right = subL;subL->_parent = ppNode;}parent->_bf = subL->_bf = 0;}/*//左右旋转void RotateLR(Node* parent){Node* cur = parent->_left;RotateL(cur);RotateR(cur);}//右左旋转void RotateRL(Node* parent){Node* cur = parent->_right;RotateR(cur);RotateL(cur);}*//***************经过细分情况的双旋*****************///左右旋void RotateLR(Node* parent){Node* subL = parent->_left;Node* subLR = subL->_right;int bf = subLR->_bf;RotateL(parent->_left);RotateR(parent);if(bf == 1){subL->_bf = -1;parent->_bf = 0;}else if(bf == -1){subL->_bf = 0;parent->_bf = 1;}else {subL->_bf = parent->_bf = 0;}subLR->_bf = 0;}//右左旋void RotateRL(Node* parent){Node* subR = parent->_right;Node* subRL = subR->_left;int bf = subRL->_bf;RotateR(parent->_right);RotateL(parent);if(bf == 1){subR->_bf = 0;parent->_bf = -1;}else if(bf == -1){subR->_bf = 1;parent->_bf = 0;}else{subR->_bf = parent->_bf = 0;}subRL->_bf = 0;}//求平衡二叉树的高度size_t Depth(){return _Depth(_root);}size_t _Depth(Node* &root){if(root == NULL)return 0;size_t leftD = _Depth(root->_left);size_t rightD = _Depth(root->_right);return leftD>rightD ? leftD+1:rightD+1;}protected:Node* _root;};2)测试用例
#include"AVLTree.h"void Test(){AVLTree<int,int> t1;int a[] = {16,3,7,11,9,26,18,14,15};for(int i = 0; i<(sizeof(a)/sizeof(a[0])); i++){t1.Insert(a[i],i);}t1.InOrder();cout<<"IsBalance: "<<t1.IsBalance()<<endl;//AVLTree<int,int> t2;//int a[] = {4, 2, 6, 1, 3, 5, 15, 7, 16, 14};//for(int i = 0; i<(sizeof(a)/sizeof(a[0])); i++)//{//t2.Insert(a[i],i);////cout<<"IsBalance: "<<t2.IsBalance()<<endl;//} //t2.InOrder();//cout<<"IsBalance: "<<t2.IsBalance()<<endl;}int main(){Test();return 0;}
【说明】:删除和插入类似,我这里就不进行详细的说明了,感兴趣的同学自己可以实现下
- 数据结构::浅析平衡二叉树
- 数据结构 - 平衡二叉树
- 数据结构: 平衡二叉树
- 数据结构 平衡二叉树
- 【数据结构】平衡二叉树
- 数据结构&&平衡二叉树
- 数据结构--平衡二叉树
- 数据结构--平衡二叉树
- 数据结构---平衡二叉树
- 数据结构--平衡二叉树
- 数据结构 平衡二叉树
- 数据结构-平衡二叉树
- 数据结构之平衡二叉树
- 数据结构 - 平衡二叉树 AVL
- 数据结构之 平衡二叉树
- 【数据结构】平衡二叉树_AVLTree
- 数据结构之平衡二叉树
- 数据结构之平衡二叉树
- oracle分析函数技术详解(配上开窗函数over())
- 百练2801解题报告---填词
- pat-a1024. Palindromic Number (25
- Boolean.valueOf(true)和true的区别
- vue项目,在进行点击的时候,如何屏蔽掉一些小的按钮,阻止冒泡
- 数据结构::浅析平衡二叉树
- 乒乓球-洛谷 1042
- 线性规划与网络流24——魔术球问题
- android加固系列—3.加固前先学会破解,静态修改so
- Android面试题
- javaweb 登陆aes-公钥加密
- eclipse之护眼
- Material Design 学习(一)
- STM32开发环境搭建