AVL树学习
学习重点:
- AVL树的定义 2. AVL树插入操作 3. 插入新结点后如何调整"不平衡问题" 4.查找效率分析
1.定义:
平衡二叉树:树上任一结点的左子树和右子树的高度之差不超过1
结点的平衡因子 = 左子树高度 - 右子树高度
只要有任何一个结点的平衡因子的绝对值大于1,那此树就不是平衡二叉树
平衡二叉树结点:
typedef struct AVLNode{
int key;
int balance; //平衡因子
struct AVLNode *lchild, *rchild;
}AVLNode, *AVLTree;
2.调平衡思想
每次插入一个结点,其祖先结点的平衡因子都会改变,从插入结点往上走,第一个平衡因子大于1的结点称为trouble finder,而插入结点称之为trouble maker,此时以trouble finder为根结点的树称为最小不平衡子树
注意:每次调整的对象都是最小不平衡子树
3.调整最小不平衡子树A的方法
分为四种不同情况讨论:
- LL 在A的左孩子的左子树插入结点导致不平衡
- RR 在A的右孩子的右子树中插入导致不平衡
- LR 在A的左孩子的右子树中插入导致不平衡
- RL 在A的右孩子的左子树中插入导致不平衡
1.LL:
调整目标:1.恢复最小不平衡子树A的平衡 2.保持二叉排序树的特性
LL平衡旋转:(右单旋转)由于在结点A的左孩子的左子树上插入了新结点,A的平衡因子由1增至2,导致以A为根的子树失去平衡,需要一次向右的旋转操作。将A的左孩子B向右上旋转代替A称为根结点,将A向右下旋转成为B的右子树的根结点,B的原右子树则作为A结点的左子树
还有一种理解方法:找到当前的最小不平衡子树后,确定要旋转的结点为三个(trouble maker trouble finder以及他们的连接者),根据结点的平衡因子,谁的平衡因子的值在三个待旋转的结点的中间,谁就作为根结点,其余结点根据左右位置判断大小分配位置(也可以根据平衡因子判断,平衡因子最大的在右边,平衡因子最小的在左边)
2.RR
RR平衡旋转:(左单旋转)由于在结点A的右孩子的右子树上插入了新结点,A的平衡因子由-1减至-2,导致以A为根的子树失去平衡,需要一次向左的旋转操作。将A的右孩子B向左上旋转成为根结点,A结点向左下旋转成为B的左子树的根结点,而B的原左子树则作为A结点的右子树
以上两个涉及的旋转都是单旋转
3.LR
LR平衡旋转:(先左后右双旋转)先将A结点的左孩子B的右子树的根结点C向左上旋转提升到B结点的位置,然后再把该C结点向右上旋转提升到A结点的位置
4.RL
RL平衡旋转:(先有后左双旋转)**先将A结点的右孩子B的左子树的根结点C向右上旋转提升到B结点的位置。然后再把该C结点向左上旋转提升到A结点的位置
注意:在最小平衡子树中,左孩子只能进行右上旋,右孩子只能进行左上旋
接下来,是AVL树的具体实现以及调平衡功能的实现
功能实现代码:
功能描述:根据输入的一组序列生成AVL树,一边构造树,一边调整平衡,最终得到一棵AVL树
/*平衡二叉树代码实现*/
#include<stdio.h>
#include<stdlib.h>
#include<iostream>
/*平衡二叉树结构定义*/
typedef struct Node{
int data;
int bal; //平衡因子
struct Node *lchild, *rchild, *parent; // 比一般的搜索树多了父亲结点
}BiNode, *BiTree;
/*先实现两种单旋转方法*/
/*右单旋转实现(LL型): 核心思想就是把左侧结点旋转上来,成为新的根结点,
而原先的根结点成为它的右孩子结点,再根据BST的原则把下层的子树分配好位置*/
void RotateRight(BiTree &parent, BiTree &T){
/*传入的parent是最小不平衡子树的根结点,T是整棵树的根结点*/
BiNode* this_father = parent->parent; // 记录最小不平衡子树根结点的父结点
BiNode* SubL = parent->lchild; // 记录左孩子
BiNode* SubLR = SubL->rchild; // 记录左孩子的右孩子
// 令SubLR为parent的左孩子
parent->lchild = SubLR;
if(SubLR){
SubLR->parent = parent;
}
//将SubL旋上来,成为新的根结点
SubL->rchild = parent;
parent->parent = SubL;
// 接下来,更新this_father的孩子结点(从parent换成SubL)
if(!this_father){ // 如果this_father不存在,则说明parent就是整棵树的根结点
T = SubL;
SubL->parent = NULL; //往上没有了
}
else{ // 若存在,则需要判断是左孩子还是右孩子
if(this_father->lchild == parent){
this_father->lchild = SubL;
}
else{
this_father->rchild = SubL;
}
SubL->parent = this_father;
}
}
/*左单旋实现(RR型): 与LL型的旋转方法完全对称*/
void RotateLeft(BiTree &parent, BiTree &T){
/*parent是最小不平衡子树的根结点,T是该树的根结点*/
BiNode* this_father = parent->parent;
BiNode* SubR = parent->rchild;
BiNode* SubRL = SubR->lchild; // 记录右孩子的左孩子(因为这个结点要换地方)
parent->rchild = SubRL;
if(SubRL){
SubRL->parent = parent;
}
SubR->lchild = parent;
parent->parent = SubR; // SubR成为新的根结点
if(!this_father){
T = SubR;
SubR->parent = NULL;
}
else{
if(this_father->lchild == parent){
this_father->lchild = SubR;
}
else{
this_father->rchild = SubR;
}
SubR->parent = this_father;
}
}
// 接下来实现双旋转(LR RL型)
void RotateLR(BiTree &parent, BiTree &T){
/*先对SubL进行左旋变化,在对parent进行右旋变化
注意:对SubL左旋变化时,把它当成最小不平衡子树的根结点!!*/
BiNode* SubL = parent->lchild;
RotateLeft(SubL, T);
RotateRight(parent, T);
}
void RotateRL(BiTree &parent, BiTree &T){
BiNode* SubR = parent->rchild;
RotateRight(SubR, T);
RotateLeft(parent, T);
}
/*求平衡因子操作实现*/
/*计算树的高度(递归实现)*/
int Height(BiTree T){
int lheight, rheight;
if(T == NULL){
return 0;
}
rheight = Height(T->rchild);
lheight = Height(T->lchild);
if(lheight > rheight){
return lheight+1;
}
else{
return rheight+1;
}
}
/*平衡因子= 右子树高度 - 左子树高度 (只是这么设定)*/
void CalBalance(BiTree T){
if(T == NULL){
return;
}
T->bal = Height(T->rchild) - Height(T->lchild);
CalBalance(T->lchild);
CalBalance(T->rchild); // 递归计算,计算所有结点的平衡因子
}
/*接下来就是平衡二叉树的插入操作
根据给定的数据序列,插入生成平衡二叉树,并在插入结束后判断是否平衡
如果不平衡,就需要根据不平衡问题的类别进行旋转调平衡*/
/*失衡分析:设插入结点为p, 插入结点的父结点为x,现考察x结点的parent结点
1. 当abs(parent->bal) == 1, 树保持平衡,但以parent为根结点的子树高度增加1,所以
此时需要考虑parent的父结点是否失衡(也就是需要向上回溯)
2. 当abs(parent->bal) == 0, 树保持平衡,且以parent为根结点的子树高度不变,故而不需要
向上回溯
3. 当abs(parent->bal) > 1, 树失衡,此时考察导致结点parent的失衡的trouble maker在哪,
以此判断使用四种旋转类型的那种进行调平衡*/
void ToBalance(BiTree &x, BiTree &T){ // 插入后调整平衡函数
/*x结点是插入结点的父结点*/
BiNode* parent = NULL;
// 若插入结点不是树的根结点
if(x){
parent = x->parent; // 考察其parent结点
while(parent){
// 情形3
if(parent->bal < -1){ // 左孩子问题
if(parent->lchild->bal == -1){
// 说明trouble发生在左孩子的左子树,即LL型
RotateRight(parent, T);
}
else{
// trouble发生在左孩子的右子树,即LR型
RotateLR(parent, T);
}
break; // 旋转完要break退出循环
}
if(parent->bal > 1){ // 右孩子问题
if(parent->rchild->bal == 1){
//trouble发生在右孩子的右子树上,即RR型
RotateLeft(parent, T);
}
else{
// trouble发生在右孩子的左子树上,即RL型
RotateRL(parent, T);
}
break;
}
// 情形2: 不需要回溯(树的高度没变)
else if(parent->bal == 0){
break;
}
// 情形1
parent = parent->parent; // 回溯:往前判断
}
}
}
/*结合调平衡函数和计算平衡因子的函数设计插入结点函数*/
BiNode* NewNode(int data){
BiNode* p = new BiNode;
p->bal = 0;
p->data = data;
p->lchild = p->rchild = p->parent = NULL;
return p;
}
void Insert(BiTree &T, int data){
BiNode* parent = NULL; // 记录插入结点的父结点
BiNode* p = T;
if(T == NULL){
T = NewNode(data);
}
else{
while(p){
parent = p; // 记录父亲结点
if(p->data == data){
//存在一样值的已有结点
return;
}
else if(p->data > data){
p = p->lchild;
}
else{
p = p->rchild;
}
}
p = NewNode(data);
if(parent->data > data){
parent->lchild = p;
}
else{
parent->rchild = p;
}
p->parent = parent;
}
// 更新平衡因子并维护平衡
CalBalance(T);
ToBalance(parent, T);
CalBalance(T);
}
void CreateTree(BiTree &T, int str[], int len){
for(int i = 0;i < len; i++){
Insert(T, str[i]);
}
}
void PreOrder(BiTree &T){
if(T){
std::cout << T->data << ",";
PreOrder(T->lchild);
PreOrder(T->rchild);
}
}
/*删除操作,后续再进行学习*/
int main(){
int number, len = 0;
int str[200000];
char x;
while(std::cin >> number >> x){
str[len] = number;
len++;
}
BiTree T = NULL;
CreateTree(T, str, len);
PreOrder(T);
return 0;
}
python实现:
import re
class TreeNode(object):
def __init__(self, data, left=None, right=None):
self.data = data
self.left = left
self.right = right
self.height = 0
class AVLTree(object):
def __init__(self):
self.root = None
def find(self, key):
if not self.root:
return None
else:
return self._find(key, self.root)
def _find(self, key, node):
if not node:
return None
elif key < node.data:
return self._find(key, node.left)
elif key > node.data:
return self._find(key, node.right)
else:
return node
def findMin(self):
if self.root is None:
return None
else:
return self._findMin(self.root)
def _findMin(self, node):
if node.left:
return self._findMin(node.left)
else:
return node
def findMax(self):
if self.root is None:
return None
else:
return self._findMax(self.root)
def _findMax(self, node):
if node.right:
return self._findMax(node.right)
else:
return node
def height(self, node):
if node is None:
return -1
else:
return node.height
#在node节点的左孩子k1的左子树添加了新节点,左旋转
def singleLeftRotate(self, node):
k1 = node.left
node.left = k1.right
k1.right = node
node.height = max(self.height(node.right), self.height(node.left)) + 1
k1.height = max(self.height(k1.left), node.height) + 1
return k1
#在node节点的右孩子k1的右子树添加了新节点,右旋转
def singleRightRotate(self, node):
k1 = node.right
node.right = k1.left
k1.left = node
node.height = max(self.height(node.right), self.height(node.left)) + 1
k1.height = max(self.height(k1.right), node.height) + 1
return k1
#在node节点的左孩子的右子树添加了新节点,先左后右
def doubleRightRotate(self, node):
node.right = self.singleLeftRotate(node.right)
return self.singleRightRotate(node)
#在node节点的右孩子的左子树添加了新节点,先右后左
def doubleLeftRotate(self, node):
node.left = self.singleRightRotate(node.left)
return self.singleLeftRotate(node)
def insert(self, key):
if not self.root:
self.root = TreeNode(key)
else:
self.root = self._insert(key, self.root)
def _insert(self, key, node):
if node is None:
node = TreeNode(key)
elif key < node.data:
node.left = self._insert(key, node.left)
if (self.height(node.left) - self.height(node.right)) == 2:
if key < node.left.data:
node = self.singleLeftRotate(node)
else:
node = self.doubleLeftRotate(node)
elif key > node.data:
node.right = self._insert(key, node.right)
if (self.height(node.right) - self.height(node.left)) == 2:
if key > node.right.data:
node = self.singleRightRotate(node)
else:
node = self.doubleRightRotate(node)
node.height = max(self.height(node.right), self.height(node.left)) + 1
return node
def delete(self, key):
if self.root is None:
raise KeyError('Error,empty tree')
else:
self.root = self._delete(key, self.root)
def _delete(self, key, node):
if node is None:
raise KeyError('Error,key not in tree')
elif key < node.data:
node.left = self._delete(key, node.left)
if (self.height(node.right) - self.height(node.left)) == 2:
if self.height(node.right.right) >= self.height(node.right.left):
node = self.singleRightRotate(node)
else:
node = self.doubleRightRotate(node)
node.height = max(self.height(node.left), self.height(node.right)) + 1
elif key > node.data:
node.right = self._delete(key, node.right)
if (self.height(node.left) - self.height(node.right)) == 2:
if self.height(node.left.left) >= self.height(node.left.right):
node = self.singleLeftRotate(node)
else:
node = self.doubleLeftRotate(node)
node.height = max(self.height(node.left), self.height(node.right)) + 1
elif node.left and node.right:
if node.left.height <= node.right.height:
minNode = self._findMin(node.right)
node.key = minNode.key
node.right = self._delete(node.key, node.right)
else:
maxNode = self._findMax(node.left)
node.key = maxNode.key
node.left = self._delete(node.key, node.left)
node.height = max(self.height(node.left), self.height(node.right)) + 1
else:
if node.right:
node = node.right
else:
node = node.left
return node
# 先序遍历
def preOrderTraverse(self, node):
if node is not None:
print(node.data, end=',')
self.preOrderTraverse(node.left)
self.preOrderTraverse(node.right)
if __name__ == '__main__':
str = input()
a = []
a = re.findall(r'\d+\.?\d*', str) # 正则表达式获得一行字符串的所有数字
a = list(map(int, a))
avl = AVLTree()
for i in a:
avl.insert(i)
avl.preOrderTraverse(avl.root)