AVL 树是带有平衡条件的二叉搜索树。当它不平衡时重排其节点让其再度保持平衡。
平衡节点(balanced node)或者说平衡因子( balance factor)如果节点是平衡树的节点,即它的两颗子树的高度差不大于1,则该节点是平衡的。
BalanceFactor = height(left-sutree) − height(right-sutree)
AVL树的旋转
在AVL树中,每次插入或删除后,则需要检查每个节点的平衡因子,如果每个节点都满足平衡的条件,则可以完成操作,否则需要使用旋转操作重新平衡树。
旋转分为两种类型,
- 单旋转
- 左旋转(LL旋转)
- 右旋转(RR旋转)
- 双旋转
- 左右旋转(LR旋转)
- 右左旋转(RL旋转)
分别在下列情况下使用
- 如果左侧子树的右子树存在不平衡则执行左右旋转
- 如果左侧子树的左子树存在不平衡则执行右旋转
- 如果右侧子树的右子树存在不平衡则执行坐旋转
- 如果右侧子树的左子树存在不平衡则执行右左旋转
左旋转(LL旋转)
当一个节点插入到节点A的右子树的右子树位置时,如果树变的不平衡,则执行左旋转。
Alogrithm rotateLeft(nodeN) //nodeN 就如上图的A节点//修正给定的节点nodeN的不平衡,因为在nodeN的右子树节点的右子树位置添加了节点C//将A的右子树赋值给新的变量nodeC 也就是B节点nodeC = nodeN->rightChild//将nodeC的左子树赋值给nodeN的右子树 也就是将 将B的左子树赋值给A的右节点nodeN.rightChild = nodeC.leftChild//将nodeN赋值给nodeC的左子树 将B 的左子树设置为AnodeC->leftChild = nodeN将nodeC返回 也就是 Breturn nodeC
右旋转(RR旋转)
将一个节点插入到节点C的左子树的左子树位置时,AVL树可能变的不平衡,此时需要右旋转
Alogrithm rotateRight(nodeN) //nodeN 就如上图的C节点//修正给定的节点nodeN的不平衡,因为在nodeN的左子树节点的右子树位置添加了节点A//将C的左子树赋值给新的变量nodeC 也就是B节点nodeC = nodeN->leftChild//将nodeC的右子树赋值给nodeN的左子树 也就是将 将B的右子树赋值给C的左子树nodeN.leftChild = nodeC.rightChild//将nodeN赋值给nodeC的右子树 将B 的右子树设置为C节点nodeC->rightChild = nodeN将nodeC返回 也就是 Breturn nodeC
左右旋转(LR旋转)
如果在节点C左子树节点的右子树位置插入节点时,树变的不平衡,则执行左右旋转。左右旋转顾明思义,先左旋转再右旋转
Alogrithm rotateLeftRight(nodeN) nodeN代表上图中的C//修正给定的节点nodeN的不平衡,因为在nodeNd的左子树节点的右子树位置插入了新节点 B//将nodeN(C)节点左子树 赋值给一个新的辅助变量nodeC (等于节点A)nodeC = nodeN->leftChild//对节点A 进行左旋转,并将返回值赋值给nodeN(C)节点的左子树nodeN->leftChild = rotateLeft(nodeC);//再对节点nodeN(C)进行右旋转return rotateRight(nodeN)
右左旋转(RL旋转)
如果在节点A的右子树的左子树位置插入节点时,树变的不平衡,则执行右左旋转。就是先右旋转再左旋转
Alogrithm rotateRightLeft(nodeN) nodeN代表上图中的A//修正给定的节点nodeN的不平衡,因为在nodeNd的右子树节点的左子树位置插入了新节点 B//将nodeN(A)节点的右子树 赋值给一个新的辅助变量nodeC (等于节点C)nodeC = nodeN->rightChild//对节点C 进行右旋转,并将返回值赋值给nodeN(A)节点的右子树nodeN->rightChild = rotateRight(nodeC);//再对节点nodeN(A)进行左旋转return rotateRLeft(nodeN)
再平衡算法
Alogrithm rebalance(nodeN)if(nodeN的左子树的高度与其右子树的高度查大于1){// 在nodeN的左子树中添加if(nodeN的左孩子的左子树高于其右子树){rotateRight(nodeN) //在左孩子的左子树中添加}else {rotateLeftRight(nodeN) // 在左孩子的右子树中添加}}else if(nodeN的右子树的高度与其左子树的高度差大于1){if(nodeN的右孩子的右子树比其左子树高){rotateLeft(nodeN) // 在右孩子的右子树中添加}else {rotateRightLeft(nodeN) // 在右孩子的左子树中添加}}
#ifndef AVL_TREE_H#define AVL_TREE_H#include <stdio.h>#include <stdlib.h>#include <math.h>#include <assert.h>struct AVLNode;typedef struct AVLNode *AvlNode;typedef struct AVLNode *AVLTree;// 创建节点AVLTree createAvlNode(const int data);// 查找指定数据的节点AvlNode find(const int data, const AVLTree tree);// 查找最小节点AvlNode findMin(const AVLTree tree);// 查找最大节点AvlNode findMax(const AVLTree tree);// 插入数据AVLTree insert(const int data, AVLTree tree);// 删除AVLTree deleteNode(const int data, AVLTree tree);// 旋转// 左旋转(LL旋转)AVLTree rotateLeft(AVLTree node);// 右旋转(RR旋转)AVLTree rotateRight(AVLTree node);// 右左旋转(RL旋转)AVLTree rotateRightLeft(AVLTree node);// 左右旋转(LR旋转)AVLTree rotateLeftRight(AVLTree node);// 再平衡 通过旋转让AVL 树再度平衡AVLTree rebalance(const int data, AVLTree node);// 节点 structstruct AVLNode{/* data */int data;int height;AVLTree left;AVLTree right;};AVLTree createAvlNode(const int data){AVLTree node = (AVLTree)malloc(sizeof(AVLTree));if (node == NULL){printf("createAvlNode 内存分配出错!\n");exit(EXIT_FAILURE);}node->data = data;node->left = node->right = NULL;node->height = 0;return node;};// 获取avl树的高度static int height(AvlNode node){if (node == NULL)return -1;return node->height;}static int maxNumber(const int a, const int b){return a > b ? a : b;};static int getMaxHeight(AVLTree node){return maxNumber(height(node->left), height(node->right)) + 1;}// 递归查找左节点 为最小AVLTree findMin(AVLTree tree){if (tree == NULL)return NULL;if (tree->left == NULL)return tree;return findMin(tree->left);}// 循环查找右节点 为最大AVLTree findMax(AVLTree tree){if (tree != NULL){while (tree->right != NULL){tree = tree->right;}}return tree;}// 左旋转(LL旋转)AVLTree rotateLeft(AVLTree node){// 拿出右子树节点AVLTree rightNode = node->right;assert(rightNode != NULL);// 将右子树的左子树赋值给当前旋转节点的有右节点node->right = rightNode->left;//将 当前旋转节点赋值给右子树的左节点rightNode->left = node;// 重新设置高度node->height = getMaxHeight(node);rightNode->height = getMaxHeight(rightNode);//完成旋转 将右子树返回return rightNode;}// 右旋转(RR旋转)AVLTree rotateRight(AVLTree node){ // 拿出左子树节点AVLTree leftNode = node->left;assert(leftNode != NULL);// 将左子树的右子树节点赋值给当前旋转节点的左侧节点node->left = leftNode->right;// 将当前旋转节点赋值给左子树的右侧节点leftNode->right = node;// 重新设置高度node->height = getMaxHeight(node);leftNode->height = getMaxHeight(leftNode);// 完成旋转 将左子树节点返回return leftNode;};// 左右旋转(LR旋转)AVLTree rotateLeftRight(AVLTree node){assert(node != NULL && node->left != NULL);// 先对当前旋转节点的左子树节点进行左旋转 并将返回值赋值给左子树node->left = rotateLeft(node->left);// 再对当前旋转节点进行右旋转return rotateRight(node);}// 右左旋转(RL旋转)AVLTree rotateRightLeft(AVLTree node){assert(node != NULL && node->right != NULL);// 先对当前旋转节点的右子树节点进行右旋转 并将返回值赋值给右子树node->right = rotateRight(node->right);// 再对当前旋转节点进行左旋转return rotateLeft(node);}// 再平衡 通过旋转让AVL 树再度平衡AVLTree rebalance(const int data, AVLTree node){if (node == NULL)return NULL;// 两颗子树的高度差不大于1,则该节点是平衡的// 左子树的高度大于右子树的高度 并且差值大于1 节点不平衡if (height(node->left) - height(node->right) > 1){// 当前插入的值小于 左子树的值 说明 这个新节点插入到了左子树的左子树节点的位置 进行右旋转if (data < node->left->data){node = rotateRight(node);}else // 否则就是插入了 左子树的右子树节点位置 进行左右旋转{node = rotateLeftRight(node);}}// 右子树的高度大于左子树的高度 并且差值大于1 节点不平衡else if (height(node->right) - height(node->left) > 1){// 当前插入的值大于 右子树的值 说明 这个新节点插入到了 右子树的右子树的位置 进行左旋转if (data > node->right->data){node = rotateLeft(node);}else // 否则就是在右子树的左子树位置 进行右左旋转{node = rotateRightLeft(node);}}return node;};AVLTree insert(const int data, AVLTree tree){if (tree == NULL){tree = createAvlNode(data);}else if (data < tree->data){ //比当前节点小 插入左子树中tree->left = insert(data, tree->left);}else if (data > tree->data){// 比当前节点的值大,插入右子树中tree->right = insert(data, tree->right);}// 再平衡tree = rebalance(data, tree);tree->height = getMaxHeight(tree);return tree;}// 删除节点AVLTree deleteNode(const int data, AVLTree tree){if (tree == NULL)return NULL;if (tree->data > data){// 小于当前节点值 去左子树节点查找tree->left = deleteNode(data, tree->left);}else if (tree->data < data){// 大于当前节点值 右去子树节点查找tree->right = deleteNode(data, tree->right);}else if (tree->left && tree->right){// 找到右子树最小的节点,替换当前节点AVLTree temp = findMin(tree->right);tree->data = temp->data;// 再将最小节点删除tree->right = deleteNode(temp->data, tree->right);}else{AVLTree temp = tree;if (tree->left == NULL){tree = tree->right;}else if (tree->right == NULL){tree = tree->left;}free(temp);}// 再平衡tree = rebalance(data, tree);return tree;};void printTree(AVLTree tree, int level){if (tree != NULL){printTree(tree->left, level + 1);printf("node data = %d, height = %d , level = %d\n", tree->data, tree->height, level);printTree(tree->right, level + 1);}}#endif //AVL_TREE_H
AVL树的插入和删除
avl树的插入和删除是与二叉搜索树一致的,不同的是在插入或者删除节点之后,调用 rebalance 函数判断是否需要进行旋转再平衡。
