数据结构::浅析平衡二叉树

来源:互联网 发布: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;}

【说明】:删除和插入类似,我这里就不进行详细的说明了,感兴趣的同学自己可以实现下







0 0
原创粉丝点击