AVL树(平衡二叉查找树)
2014-09-13 20:47
399 查看
1、AVL树的定义
平衡二叉查找树,又称作AVL树(以提出此树的两人人名命名的),AVL树是一种高度平衡的二叉查找树,它或者是一颗空树,或者是具有下列性质的二叉查找树:
(1)它的左子树和右子树都是平衡二叉查找树
(2)它的左子树和右子树的深度差的绝对值不超过1
将二叉树上的节点的左子树的深度减去右子树的深度的值定义为节点的平衡因子,因此平衡因子的值只可能是:-1、0 和 1。
2、AVL树的插入和删除
AVL树也是二叉查找树,因此满足二叉查找树的性质。AVL树最大的特点就是高度的平衡性,因为其节点的左右子树高度差不超过1,这样就能绝对的保证AVL树的高度为O(lgn),同红黑树一样,在插入和删除节点后,AVL树的平衡性可能会被破坏,为了保持AVL树的性质,必须在插入和删除节点后进行额外的修复操作。
2.1、插入
AVL树插入节点跟二叉查找树相似,只是在一个新结点插入后,可能导致树中的一些节点不平衡(节点的平衡因子绝对值超过1)或者平衡因子改变,因此需要调整,这个调整是从插入节点开始至下向上进行;因为插入新节点后,节点的深度可能改变了,因此需要向上回溯,如有改变需要对节点的平衡因子进行修改,如果节点的平衡因子改变但是深度却没变,则回溯结束,平衡性调整结束;如果出现了节点失去平衡,则通常情况下,只需要将失去平衡的最小子树根节点进行平衡性调整即可,因为当对最小不平衡子树进行调整之后树的深度保持不变,因此这颗子树以上的节点的平衡因子都没有改变,因此整棵AVL树保持了平衡性,调整也就结束了。平衡性调整需要用到左旋和右旋,这与红黑树中的旋转是一样的。
平衡性调整分为以下四种(假设最小不平衡子树的节点为node):
(1)插入新节点后,node的平衡因子变成了2,新节点插入到了node的左孩子的左子树上,则直接以node为支点进行右旋即可。
(2)插入新节点后,node的平衡因子变成了2,新节点插入到了node的左孩子的右子树上,则需要先以node的左孩子为支点进行左旋,然后再以node为支点进行右旋即可。
(3)插入新节点后,node的平衡因子变成了-2,新节点插入到了node的右孩子的左子树上,则需要先以node的右孩子为支点进行右旋,然后再以node为支点进行左旋即可。
(4)插入新节点后,node的平衡因子变成了-2,新节点插入到了node的右孩子的右子树上,则直接以node为支点进行左旋即可。
其中(1)和(2)属于左平衡处理,因为新节点是插入到了node的左子树中导致node不平衡的;(3)和(4)属于右平衡处理,因为新节点是插入到了node的右子树中导致node的不平衡。
2.2、删除
AVL树的删除也跟二叉查找树的删除相似,只是在删除一个新节点后,会导致一些节点的平衡因子改变,也可能导致一些节点失去平衡,因此需要进行平衡性调整,同插入相似,这个调整也是从实际删除节点的父节点开始至下向上回溯进行,修改回溯路径上的节点的平衡因子,如果节点的深度没变,则回溯结束;如果节点失去平衡,则进行平衡性调整,但是同插入节点不同的是,对节点进行了平衡性调整之后,节点的深度往往会减少1,所以需要继续向上回溯调整。
平衡性调整分为以下四种(假设不平衡的节点为node):
(1)删除节点后,node的平衡因子变成了2,node的左孩子的平衡因子为0或者1,则直接以node为支点进行右旋即可。
(2)删除节点后,node的平衡因子变成了2,node的左孩子的平衡因子为-1,则需要先以node的左孩子为支点进行左旋,然后以node为支点进行右旋即可。
(3)删除节点后,node的平衡因子变成了-2,node的右孩子的平衡因子为0或者-1,则直接以node为支点进行左旋即可。
(4)删除节点后,node的平衡因子变成了-2,node的左孩子的平衡因子为1,则需要先以node的右孩子为支点进行右旋,然后以node为支点进行左旋即可。
同样,(1)和(2)属于左平衡处理,(3)和(4)属于右平衡处理。
AVL树的操作代码如下:
平衡二叉查找树,又称作AVL树(以提出此树的两人人名命名的),AVL树是一种高度平衡的二叉查找树,它或者是一颗空树,或者是具有下列性质的二叉查找树:
(1)它的左子树和右子树都是平衡二叉查找树
(2)它的左子树和右子树的深度差的绝对值不超过1
将二叉树上的节点的左子树的深度减去右子树的深度的值定义为节点的平衡因子,因此平衡因子的值只可能是:-1、0 和 1。
2、AVL树的插入和删除
AVL树也是二叉查找树,因此满足二叉查找树的性质。AVL树最大的特点就是高度的平衡性,因为其节点的左右子树高度差不超过1,这样就能绝对的保证AVL树的高度为O(lgn),同红黑树一样,在插入和删除节点后,AVL树的平衡性可能会被破坏,为了保持AVL树的性质,必须在插入和删除节点后进行额外的修复操作。
2.1、插入
AVL树插入节点跟二叉查找树相似,只是在一个新结点插入后,可能导致树中的一些节点不平衡(节点的平衡因子绝对值超过1)或者平衡因子改变,因此需要调整,这个调整是从插入节点开始至下向上进行;因为插入新节点后,节点的深度可能改变了,因此需要向上回溯,如有改变需要对节点的平衡因子进行修改,如果节点的平衡因子改变但是深度却没变,则回溯结束,平衡性调整结束;如果出现了节点失去平衡,则通常情况下,只需要将失去平衡的最小子树根节点进行平衡性调整即可,因为当对最小不平衡子树进行调整之后树的深度保持不变,因此这颗子树以上的节点的平衡因子都没有改变,因此整棵AVL树保持了平衡性,调整也就结束了。平衡性调整需要用到左旋和右旋,这与红黑树中的旋转是一样的。
平衡性调整分为以下四种(假设最小不平衡子树的节点为node):
(1)插入新节点后,node的平衡因子变成了2,新节点插入到了node的左孩子的左子树上,则直接以node为支点进行右旋即可。
(2)插入新节点后,node的平衡因子变成了2,新节点插入到了node的左孩子的右子树上,则需要先以node的左孩子为支点进行左旋,然后再以node为支点进行右旋即可。
(3)插入新节点后,node的平衡因子变成了-2,新节点插入到了node的右孩子的左子树上,则需要先以node的右孩子为支点进行右旋,然后再以node为支点进行左旋即可。
(4)插入新节点后,node的平衡因子变成了-2,新节点插入到了node的右孩子的右子树上,则直接以node为支点进行左旋即可。
其中(1)和(2)属于左平衡处理,因为新节点是插入到了node的左子树中导致node不平衡的;(3)和(4)属于右平衡处理,因为新节点是插入到了node的右子树中导致node的不平衡。
2.2、删除
AVL树的删除也跟二叉查找树的删除相似,只是在删除一个新节点后,会导致一些节点的平衡因子改变,也可能导致一些节点失去平衡,因此需要进行平衡性调整,同插入相似,这个调整也是从实际删除节点的父节点开始至下向上回溯进行,修改回溯路径上的节点的平衡因子,如果节点的深度没变,则回溯结束;如果节点失去平衡,则进行平衡性调整,但是同插入节点不同的是,对节点进行了平衡性调整之后,节点的深度往往会减少1,所以需要继续向上回溯调整。
平衡性调整分为以下四种(假设不平衡的节点为node):
(1)删除节点后,node的平衡因子变成了2,node的左孩子的平衡因子为0或者1,则直接以node为支点进行右旋即可。
(2)删除节点后,node的平衡因子变成了2,node的左孩子的平衡因子为-1,则需要先以node的左孩子为支点进行左旋,然后以node为支点进行右旋即可。
(3)删除节点后,node的平衡因子变成了-2,node的右孩子的平衡因子为0或者-1,则直接以node为支点进行左旋即可。
(4)删除节点后,node的平衡因子变成了-2,node的左孩子的平衡因子为1,则需要先以node的右孩子为支点进行右旋,然后以node为支点进行左旋即可。
同样,(1)和(2)属于左平衡处理,(3)和(4)属于右平衡处理。
AVL树的操作代码如下:
/* * AVL树(平衡二叉查找树) */ #include <iostream> using namespace std; //定义AVL树结点 typedef struct AVLNode { int key;//关键值 int balanceFactor;//平衡因子 AVLNode *left, *right, *parent; } AVLNode, AVLTree; /* * 左旋 * 对以tree为根节点的树,以node为支点左旋 * 时间复杂度:O(1) */ void leftRotate(AVLTree* &tree, AVLNode* node) { if (! tree || ! node || ! node->right) return; AVLNode* rightChild = node->right; node->right = rightChild->left; if (rightChild->left) { rightChild->left->parent = node; } rightChild->parent = node->parent; if (! node->parent) { tree = rightChild; } else if (node == node->parent->left) { node->parent->left = rightChild; } else { node->parent->right = rightChild; } rightChild->left = node; node->parent = rightChild; } /* * 右旋 * 以tree为根节点的树,以node为支点进行右旋 * 时间复杂度:O(1) */ void rightRotate(AVLTree* &tree, AVLNode* node) { if (! tree || ! node || ! node->left) return; AVLNode* leftChild = node->left; node->left = leftChild->right; if (leftChild->right) { leftChild->right->parent = node; } leftChild->parent = node->parent; if (! node->parent) { tree = leftChild; } else if (node == node->parent->left) { node->parent->left = leftChild; } else { node->parent->right = leftChild; } leftChild->right = node; node->parent = leftChild; } /* * 左平衡处理 * 时间复杂度:O(1) */ void leftBalanceForInsert(AVLTree* &tree, AVLNode* node) { if (! tree || ! node || ! node->left) return; AVLNode* leftChild = node->left; switch (leftChild->balanceFactor) { case 1: //新插入节点是在node的左孩子的左子树上,需要做单右旋处理 node->balanceFactor = leftChild->balanceFactor = 0; rightRotate(tree, node); break; case 0: //这种情况只有一种,那就是leftChild就是新插入的结点 //否则如果leftChild是中间结点,在回溯的过程中,leftChild的平衡因子 //必然会被修改成1或者-1 // 因为是新插入的结点,所以不用做处理 break; case -1: //新插入的节点是在node的左孩子的右子树上面,需要先左旋,再右旋 //首先根据node的左孩子的右子树根的平衡因子确定旋转后的节点平衡因子 switch (leftChild->right->balanceFactor) { case -1: node->balanceFactor = 0; leftChild->balanceFactor = 1; break; case 0: node->balanceFactor = leftChild->balanceFactor = 0; break; case 1: node->balanceFactor = -1; leftChild->balanceFactor = 0; break; } leftChild->right->balanceFactor = 0; leftRotate(tree, node->left); rightRotate(tree, node); break; } } /* * 右平衡处理 * 时间复杂度:O(1) */ void rightBalanceForInsert(AVLTree* &tree, AVLNode* node) { if (! tree || ! node || ! node->right) return; AVLNode* rightChild = node->right; switch (rightChild->balanceFactor) { case 1: //新插入的节点是在node的右孩子的左子树上面,需要先右旋,再左旋 //首先根据node的右孩子的左子树根的平衡因子确定旋转后的节点平衡因子 switch (rightChild->left->balanceFactor) { case 1: node->balanceFactor = 0; rightChild->balanceFactor = -1; break; case 0: node->balanceFactor = rightChild->balanceFactor = 0; break; case -1: node->balanceFactor = 1; rightChild->balanceFactor = 0; break; } rightChild->left->balanceFactor = 0; rightRotate(tree, node->right); leftRotate(tree, node); break; case 0: //这种情况只有一种,那就是leftChild就是新插入的结点 //否则如果leftChild是中间结点,在回溯的过程中,leftChild的平衡因子 //必然会被修改成1或者-1 // 因为是新插入的结点,所以不用做处理 break; case -1: //新插入节点是在node的右孩子的右子树上,直接左旋即可 node->balanceFactor = rightChild->balanceFactor = 0; leftRotate(tree, node); break; } } /* * 左平衡处理 * 时间复杂度:O(1) */ void leftBalanceForDelete(AVLTree* &tree, AVLNode* node) { if (! tree || ! node || ! node->left) return; AVLNode* leftChild = node->left; switch (leftChild->balanceFactor) { case 1: node->balanceFactor = leftChild->balanceFactor = 0; rightRotate(tree, node); break; case 0: node->balanceFactor = 1; leftChild->balanceFactor = -1; rightRotate(tree, node); break; case -1: switch (leftChild->right->balanceFactor) { case -1: node->balanceFactor = 0; leftChild->balanceFactor = 1; break; case 0: node->balanceFactor = leftChild->balanceFactor = 0; break; case 1: node->balanceFactor = -1; leftChild->balanceFactor = 0; break; } leftChild->right->balanceFactor = 0; leftRotate(tree, node->left); rightRotate(tree, node); break; } } /* * 右平衡处理 * 时间复杂度:O(1) */ void rightBalanceForDelete(AVLTree* &tree, AVLNode* node) { if (! tree || ! node || ! node->right) return; AVLNode* rightChild = node->right; switch (rightChild->balanceFactor) { case 1: switch (rightChild->left->balanceFactor) { case 1: node->balanceFactor = 0; rightChild->balanceFactor = -1; break; case 0: node->balanceFactor = rightChild->balanceFactor = 0; break; case -1: node->balanceFactor = 1; rightChild->balanceFactor = 0; break; } rightChild->left->balanceFactor = 0; rightRotate(tree, node->right); leftRotate(tree, node); break; case 0: node->balanceFactor = -1; rightChild->balanceFactor = 1; leftRotate(tree, node); break; case -1: node->balanceFactor = rightChild->balanceFactor = 0; leftRotate(tree, node); break; } } /* * AVL树插入后的平衡性调整 * 至下向上回溯调整,整个过程共有O(lgn)次回溯和一次平衡性调整O(1) * 所以时间复杂度为:O(lgn) */ void avlInsertFixup(AVLTree* &tree, AVLNode* node) { bool isTaller = true; while (isTaller && node->parent) { if (node == node->parent->left) { switch (node->parent->balanceFactor) { case 1: leftBalanceForInsert(tree, node->parent); isTaller = false; break; case 0: node->parent->balanceFactor = 1; isTaller = true; break; case -1: node->parent->balanceFactor = 0; isTaller = false; break; } } else { switch (node->parent->balanceFactor) { case 1: node->parent->balanceFactor = 0; isTaller = false; break; case 0: node->parent->balanceFactor = -1; isTaller = true; break; case -1: rightBalanceForInsert(tree, node->parent); isTaller = false; break; } } node = node->parent; } } /* * 插入节点 * 同BST的插入相类似,只是插入后需要做调整以保证AVL树的性质 * 插入节点到树中需要时间O(lgn),平衡性调整的时间:O(lgn) * 所以总的时间复杂度为:O(lgn) */ void avlInsert(AVLTree* &tree, AVLNode* node) { if (! node) return; AVLNode* pos = NULL; AVLNode* temp = tree; while (temp) { pos = temp; if (node->key < temp->key) { temp = temp->left; } else { temp = temp->right; } } node->parent = pos; if (! pos) { tree = node; } else if (node->key < pos->key) { pos->left = node; } else { pos->right = node; } avlInsertFixup(tree, node); } /* * 寻找AVL树中node节点子树别的最小元素 * 时间复杂度:O(lgn) */ AVLNode* avlTreeMinimum(AVLNode* node) { if (! node) return NULL; while (node->left) { node = node->left; } return node; } /* * 寻找AVL树中node节点的中序遍历后继 * 时间复杂度为O(lgn) */ AVLNode* avlTreeSuccessor(AVLNode* node) { if (! node) return NULL; if (node->right) { return avlTreeMinimum(node->right); } AVLNode* parentNode = node->parent; while (parentNode && node == parentNode->right) { node = parentNode; parentNode = node->parent; } return parentNode; } /* * AVl树中删除节点后的平衡性调整 * 自下而上回溯 * 整个过程包括O(lgn)次回溯和O(lgn)次平衡处理,每次O(1)时间复杂度 * 所以总的时间复杂度为:O(lgn) */ void avlDeleteFixup(AVLTree* &tree, AVLNode* node) { bool isLower = true; while (isLower && node->parent) { if (node == node->parent->left) { switch (node->parent->balanceFactor) { case 1: node->parent->balanceFactor = 0; isLower = true; break; case 0: node->parent->balanceFactor = -1; isLower = false; break; case -1: rightBalanceForDelete(tree, node->parent); isLower = true; break; } } else { switch (node->parent->balanceFactor) { case 1: leftBalanceForDelete(tree, node->parent); isLower = true; break; case 0: node->parent->balanceFactor = 1; isLower = false; break; case -1: node->parent->balanceFactor = 0; isLower = true; break; } } node = node->parent; } } /* * 删除节点,与插入节点相反对应 * 删除节点需要时间O(lgn),平衡性调整需要O(lgn) * 所以总的时间复杂度为O(lgn) */ AVLNode* avlDelete(AVLTree* &tree, AVLNode* node) { if (! tree || ! node) { return NULL; } AVLNode* delNode = NULL; if (! node->left || ! node->right) { delNode = node; } else { delNode = avlTreeSuccessor(node); } AVLNode* fillNode = NULL; if (delNode->left) { fillNode = delNode->left; } else { fillNode = delNode->right; } if (fillNode) { fillNode->parent = delNode->parent; } if (! delNode->parent) { tree = fillNode; } else if (delNode == delNode->parent->left) { delNode->parent->left = fillNode; } else { delNode->parent->right = fillNode; } if (delNode != node) { node->key = delNode->key; } avlDeleteFixup(tree, delNode); return delNode; }
相关文章推荐
- 算法系列(九)平衡二叉查找树AVL树
- 算法学习 - 平衡二叉查找树实现(AVL树)
- AVL树-自平衡二叉查找树(Java实现)
- AVL树-自平衡二叉查找树(Java实现)
- 自平衡二叉查找树(一)-----------AVL树分析和代码实现
- 算法学习 - 平衡二叉查找树实现(AVL树)
- 数据结构14.自平衡二叉查找树_AVL树
- 数据结构查找(2)--平衡的二叉查找树(AVL树)
- 数据结构14.自平衡二叉查找树_AVL树
- AVL树(平衡二叉查找树)
- AVL树(平衡二叉查找树)
- 树04-自平衡二叉查找树-AVL树
- 数据结构:自平衡二叉查找树(AVL树)
- 面试题:什么叫平衡二叉查找树--AVL树
- AVL树(平衡二叉查找树)
- AVL树—平衡二叉查找树
- 二叉查找树(BST)和平衡二叉查找树(AVL)
- 平衡二叉查找树(JAVA)
- 平衡二叉查找树插入节点操作( AVLTree ):旋转、调整平衡
- AVL树,最老的一种平衡查找树