数据结构与算法简记:AVL树

骑猪看日落 2022-07-19 05:21 237阅读 0赞

前面记录了二叉查找树,它在搜索方面的效率显而易见,可它也存在某种缺陷,假设我们连续插入较小或较大的数据,那么二叉查找树将会逐渐退变为一个线性结构,从而搜索就变为了线性查找,效率将会大打折扣。所以,我们需要一棵这样的树,它在插入新节点后,能够重新调整自己的结构,使左右恢复平衡。AVL树就符合这个条件。

AVL树是最先发明的自平衡二叉查找树,其得名于它的发明者 G.M. Adelson-Velsky 和 E.M. Landis,他们在 1962 年的论文 “An algorithm for the organization of information” 中发表了它。

在AVL树中任何节点的两个子树的高度最大差别为一,所以它也被称为高度平衡树,其查找、插入和删除在平均和最坏情况下都是O(logN)。

首先是创建AVL树:

我们可以从一个排序后的数组来初始化AVL树,这个过程不难,只需找到数组区间的中间元素,这个将作为树的根节点,同时这个节点将数组划分两个子区间,然后分别再取左右子区间中间元素,创建左右子树的根节点,同时继续划分子区间,如此进行下去。过程如下图所示:

20160810133337047

然后是插入和移除节点,基本操作跟二叉查找树相似,不同的是这两个操作都需要重新平衡子树结构。

要使一棵树重新恢复平衡,我们需要对子树节点进行旋转操作,而旋转操作需要应对4种不同的情况,它们分别是:

LL: 即左左情况,此时需要对节点进行右旋转操作
RR: 即右右情况,此时需要对节点进行左旋转操作

以上这两种情况的旋转操作如下图所示:

20160810162725249

还有两种相对复杂的情况:

LR: 即左右情况,此时需要先进行一次RR型旋转,再进行一次LL型旋转
RL: 即右做情况,此时需要先进行一次LL行旋转,再进行一次RR型旋转

下面两张图分别拆解了这两种情况的步骤:

20160810163007891

20160810163222262

下面分别是JS和C语言的实现代码:

JS描述:

  1. //AVL树节点结构
  2. function AVLTreeNode(data) {
  3. this.data = data;
  4. this.leftChild = null;
  5. this.rightChild = null;
  6. this.height = 0;
  7. }
  8. var AVLTreeUtil = {
  9. //由排序数组创建AVL树
  10. createAVLTree: function(array, low, high) {
  11. if (high < low) return null;
  12. var mid = Math.floor((low + high) / 2);
  13. var newNode = new AVLTreeNode(array[mid]);
  14. newNode.leftChild = this.createAVLTree(array, low, mid - 1);
  15. newNode.rightChild = this.createAVLTree(array, mid + 1, high);
  16. this.updateHeight(newNode);
  17. return newNode;
  18. },
  19. //前序遍历子树
  20. preOrderTraverse: function(node, visitFn) {
  21. if (node) {
  22. visitFn(node);
  23. this.preOrderTraverse(node.leftChild, visitFn);
  24. this.preOrderTraverse(node.rightChild, visitFn);
  25. }
  26. },
  27. //中序遍历子树
  28. inOrderTraverse: function(node, visitFn) {
  29. if (node) {
  30. this.inOrderTraverse(node.leftChild, visitFn);
  31. visitFn(node);
  32. this.inOrderTraverse(node.rightChild, visitFn);
  33. }
  34. },
  35. //插入节点并保持树平衡
  36. insertNodeWithBalance: function(node, key) {
  37. if (!node) {
  38. return new AVLTreeNode(key);
  39. }
  40. if (key < node.data) {
  41. node.leftChild = this.insertNodeWithBalance(node.leftChild, key);
  42. //平衡当前节点和左子树
  43. node = this.balanceLeft(node, key);
  44. } else {
  45. node.rightChild = this.insertNodeWithBalance(node.rightChild, key);
  46. //平衡当前节点和右子树
  47. node = this.balanceRight(node, key);
  48. }
  49. this.updateHeight(node);
  50. return node;
  51. },
  52. //移除子树中指定值的节点
  53. removeNodeWithBalance: function(node, key) {
  54. if (!node) return null;
  55. if (key < node.data) {
  56. node.leftChild = this.removeNodeWithBalance(node.leftChild, key);
  57. //平衡当前节点和左子树
  58. node = this.balanceLeft(node, key);
  59. this.updateHeight(node);
  60. return node;
  61. }
  62. if (key > node.data) {
  63. node.rightChild = this.removeNodeWithBalance(node.rightChild, key);
  64. //平衡当前节点和右子树
  65. node = this.balanceRight(node, key);
  66. this.updateHeight(node);
  67. return node;
  68. }
  69. //叶子节点
  70. if (!node.leftChild && !node.rightChild) {
  71. return null;
  72. }
  73. //仅左子树不存在,但存在右子树
  74. if (!node.leftChild) {
  75. return node.rightChild;
  76. }
  77. //仅右子树不存在,但存在左子树
  78. if (!node.rightChild) {
  79. return node.leftChild;
  80. }
  81. //下面处理左右子树均不为空的情况
  82. //查找右子树中最小节点
  83. var minNode = this.findMinNode(node.rightChild);
  84. //替换
  85. node.data = minNode.data;
  86. //在右子树中移除最小节点
  87. node.rightChild = this.removeNodeWithBalance(node.rightChild, minNode.data);
  88. //平衡当前节点和右子树
  89. node = this.balanceRight(node, key);
  90. this.updateHeight(node);
  91. return node;
  92. },
  93. //查找指定子树中最小节点
  94. findMinNode: function(node) {
  95. while (node && node.leftChild) {
  96. node = node.leftChild;
  97. }
  98. return node;
  99. },
  100. //获取节点树高
  101. getHeight: function(node) {
  102. if (!node) return -1;
  103. return node.height;
  104. },
  105. //更新节点树高
  106. updateHeight: function(node) {
  107. node.height = Math.max(this.getHeight(node.leftChild), this.getHeight(node.rightChild)) + 1;
  108. },
  109. //LL型右旋操作
  110. rightRotateLL: function(node) {
  111. var pivot = node.leftChild;
  112. node.leftChild = pivot.rightChild;
  113. pivot.rightChild = node;
  114. this.updateHeight(node);
  115. this.updateHeight(pivot);
  116. return pivot;
  117. },
  118. //RR型左旋操作
  119. leftRotateRR: function(node) {
  120. var pivot = node.rightChild;
  121. node.rightChild = pivot.leftChild;
  122. pivot.leftChild = node;
  123. this.updateHeight(node);
  124. this.updateHeight(pivot);
  125. return pivot;
  126. },
  127. //LR型双旋操作
  128. doubleRotateLR: function(node) {
  129. node.leftChild = this.leftRotateRR(node.leftChild);
  130. return this.rightRotateLL(node);
  131. },
  132. //RL型双旋操作
  133. doubleRotateRL: function(node) {
  134. node.rightChild = this.rightRotateLL(node.rightChild);
  135. return this.leftRotateRR(node);
  136. },
  137. //平衡左边
  138. balanceLeft: function(node, key) {
  139. if (this.lostBalance(node)) {
  140. if (key < node.leftChild.data) {
  141. node = this.rightRotateLL(node); //LL
  142. } else {
  143. node = this.doubleRotateLR(node); //LR
  144. }
  145. }
  146. return node;
  147. },
  148. //平衡右边
  149. balanceRight: function(node, key) {
  150. if (this.lostBalance(node)) {
  151. if (key > node.rightChild.data) {
  152. node = this.leftRotateRR(node); //RR
  153. } else {
  154. node = this.doubleRotateRL(node); //RL
  155. }
  156. }
  157. return node;
  158. },
  159. //是否失去平衡
  160. lostBalance: function(node) {
  161. //左右树高之差大于1就失去了平衡
  162. return Math.abs(this.getHeight(node.leftChild) - this.getHeight(node.rightChild)) > 1;
  163. }
  164. };
  165. //已排序数据
  166. var array = [1, 3, 4, 6, 7, 8, 10, 13, 15];
  167. var rootNode = AVLTreeUtil.createAVLTree(array, 0, array.length - 1);
  168. //用于存放节点遍历序列
  169. var orderArray = [];
  170. var visitFn = function(node) {
  171. orderArray.push(node.data);
  172. };
  173. var printInAndPre = function() {
  174. orderArray.length = 0;
  175. AVLTreeUtil.inOrderTraverse(rootNode, visitFn);
  176. console.log('in order:', orderArray.join(' '));
  177. orderArray.length = 0;
  178. AVLTreeUtil.preOrderTraverse(rootNode, visitFn);
  179. console.log('pre order:', orderArray.join(' '));
  180. };
  181. console.log('--- after init ---');
  182. printInAndPre();
  183. //插入节点14并使树平衡
  184. rootNode = AVLTreeUtil.insertNodeWithBalance(rootNode, 14);
  185. console.log('--- after 14 inserted ---');
  186. printInAndPre();
  187. rootNode = AVLTreeUtil.removeNodeWithBalance(rootNode, 14);
  188. console.log('--- after 14 removed ---');
  189. printInAndPre();
  190. rootNode = AVLTreeUtil.insertNodeWithBalance(rootNode, 0);
  191. rootNode = AVLTreeUtil.insertNodeWithBalance(rootNode, -1);
  192. console.log('--- after 0 and -1 inserted ---');
  193. printInAndPre();

C语言描述:

  1. #include <stdio.h>
  2. #include <stdlib.h>
  3. #define max(a,b) (a > b ? a : b)
  4. //AVL树节点结构体
  5. typedef struct node {
  6. int data;
  7. struct node *lchild, *rchild;
  8. int height;
  9. } AVLTreeNode;
  10. AVLTreeNode * createAVLTree(int *array, int low, int high);
  11. void preOrderTraverse(AVLTreeNode *node);
  12. void inOrderTraverse(AVLTreeNode *node);
  13. AVLTreeNode * findMinNode(AVLTreeNode *node);
  14. int getHeight(AVLTreeNode *node);
  15. void updateHeight(AVLTreeNode *node);
  16. AVLTreeNode * insertNodeWithBalance(AVLTreeNode *node, int key);
  17. AVLTreeNode * removeNodeWithBalance(AVLTreeNode *node, int key);
  18. AVLTreeNode * rightRotateLL(AVLTreeNode *node);
  19. AVLTreeNode * doubleRotateLR(AVLTreeNode *node);
  20. AVLTreeNode * leftRotateRR(AVLTreeNode *node);
  21. AVLTreeNode * doubleRotateRL(AVLTreeNode *node);
  22. void balanceLeft(AVLTreeNode **node, int key);
  23. void balanceRight(AVLTreeNode **node, int key);
  24. int main(int argc, const char * argv[]) {
  25. //已排序数据
  26. int array[] = {
  27. 1, 3, 4, 6, 7, 8, 10, 13, 15};
  28. int size = 9;
  29. AVLTreeNode *rootNode = createAVLTree(array, 0, size - 1);
  30. printf("---after init ---\nin: ");
  31. inOrderTraverse(rootNode);
  32. printf("\npre: ");
  33. preOrderTraverse(rootNode);
  34. rootNode = insertNodeWithBalance(rootNode, 14);
  35. printf("\n--- after 14 inserted ---\nin: ");
  36. inOrderTraverse(rootNode);
  37. printf("\npre: ");
  38. preOrderTraverse(rootNode);
  39. rootNode = removeNodeWithBalance(rootNode, 14);
  40. printf("\n--- after 14 removed ---\nin: ");
  41. inOrderTraverse(rootNode);
  42. printf("\npre: ");
  43. preOrderTraverse(rootNode);
  44. rootNode = insertNodeWithBalance(rootNode, 0);
  45. rootNode = insertNodeWithBalance(rootNode, -1);
  46. printf("\n--- after 14 removed ---\nin: ");
  47. inOrderTraverse(rootNode);
  48. printf("\npre: ");
  49. preOrderTraverse(rootNode);
  50. return 0;
  51. }
  52. //初始化AVL树
  53. AVLTreeNode * createAVLTree(int *array, int low, int high) {
  54. if(high < low) return NULL;
  55. //取出子树根节点值
  56. int mid = (low + high) / 2;
  57. //创建子树根节点
  58. AVLTreeNode *newNode = (AVLTreeNode *) malloc(sizeof(AVLTreeNode));
  59. newNode->data = array[mid];
  60. newNode->height = 0;
  61. //创建左右子树
  62. newNode->lchild = createAVLTree(array, low, mid - 1);
  63. newNode->rchild = createAVLTree(array, mid + 1, high);
  64. //更新根节点树高
  65. updateHeight(newNode);
  66. return newNode;
  67. }
  68. //先序遍历,用来验证平衡后的二叉树
  69. void preOrderTraverse(AVLTreeNode *node) {
  70. if (node != NULL) {
  71. printf("%d ", node->data);
  72. preOrderTraverse(node->lchild);
  73. preOrderTraverse(node->rchild);
  74. }
  75. }
  76. //中序遍历
  77. void inOrderTraverse(AVLTreeNode *node) {
  78. if (node != NULL) {
  79. inOrderTraverse(node->lchild);
  80. printf("%d ", node->data);
  81. inOrderTraverse(node->rchild);
  82. }
  83. }
  84. //查找指定子树中最小节点
  85. AVLTreeNode * findMinNode(AVLTreeNode *node) {
  86. while (node != NULL && node->lchild != NULL) {
  87. node = node->lchild;
  88. }
  89. return node;
  90. }
  91. //插入节点并保持平衡
  92. AVLTreeNode * insertNodeWithBalance(AVLTreeNode *node, int key) {
  93. //新增叶子节点,并返回该节点,父节点会与其建立关联
  94. if (node == NULL) {
  95. node = (AVLTreeNode *) malloc(sizeof(AVLTreeNode));
  96. node->data = key;
  97. node->height = 0;
  98. node->lchild = node->rchild = NULL;
  99. return node;
  100. }
  101. if (key < node->data) {
  102. node->lchild = insertNodeWithBalance(node->lchild, key);
  103. //平衡当前节点和左子树
  104. balanceLeft(&node, key);
  105. } else {
  106. node->rchild = insertNodeWithBalance(node->rchild, key);
  107. //平衡当前节点和右子树
  108. balanceRight(&node, key);
  109. }
  110. updateHeight(node);
  111. return node;
  112. }
  113. //在子树中移除值为key的节点并保持平衡
  114. AVLTreeNode * removeNodeWithBalance(AVLTreeNode *node, int key) {
  115. if (node == NULL) return NULL;
  116. //向左子树继续
  117. if (key < node->data) {
  118. node->lchild = removeNodeWithBalance(node->lchild, key);
  119. //平衡当前节点和左子树
  120. balanceLeft(&node, key);
  121. updateHeight(node);
  122. return node;
  123. }
  124. //向右子树继续
  125. if (key > node->data) {
  126. node->rchild = removeNodeWithBalance(node->rchild, key);
  127. //平衡当前节点和右子树
  128. balanceRight(&node, key);
  129. updateHeight(node);
  130. return node;
  131. }
  132. //以下是匹配到节点后的几个不同的情况
  133. //移除叶子节点
  134. if (node->lchild == NULL && node->rchild == NULL) {
  135. //释放
  136. free(node);
  137. return NULL;
  138. }
  139. //仅左子树不存在
  140. if (node->lchild == NULL) {
  141. AVLTreeNode *rchild = node->rchild;
  142. //释放
  143. free(node);
  144. return rchild;
  145. }
  146. //仅右子树不存在
  147. if (node->rchild == NULL) {
  148. AVLTreeNode *lchild = node->lchild;
  149. //释放
  150. free(node);
  151. return lchild;
  152. }
  153. //-----最后如果左右子节点均存在,则先找到右子树最小子节点,替换其值,然后将最小节点删除-----
  154. //查找右子树最小节点
  155. AVLTreeNode *minNode = findMinNode(node->rchild);
  156. //替换其值
  157. node->data = minNode->data;
  158. //在右子树中移除最小节点
  159. node->rchild = removeNodeWithBalance(node->rchild, minNode->data);
  160. //平衡当前节点和右子树
  161. balanceRight(&node, key);
  162. updateHeight(node);
  163. return node;
  164. }
  165. //获取子树根节点高度
  166. int getHeight(AVLTreeNode *node) {
  167. if (node == NULL) return -1;
  168. return node->height;
  169. }
  170. //更新节点树高
  171. void updateHeight(AVLTreeNode *node) {
  172. node->height = max(getHeight(node->lchild), getHeight(node->rchild)) + 1;
  173. }
  174. //对LL类型做右旋转操作
  175. AVLTreeNode * rightRotateLL(AVLTreeNode *node) {
  176. //获取左子节点作为轴点
  177. AVLTreeNode *pivot = node->lchild;
  178. node->lchild = pivot->rchild;
  179. pivot->rchild = node;
  180. updateHeight(node);
  181. updateHeight(pivot);
  182. return pivot;
  183. };
  184. //对RR类型做左旋转操作
  185. AVLTreeNode * leftRotateRR(AVLTreeNode *node) {
  186. //获取右子节点作为轴点
  187. AVLTreeNode *pivot = node->rchild;
  188. node->rchild = pivot->lchild;
  189. pivot->lchild = node;
  190. updateHeight(node);
  191. updateHeight(pivot);
  192. return pivot;
  193. };
  194. //对于LR类型,先对左子节点进行一次RR类型的左旋转操作,然后再对根节点进行一次LL类型的右旋转操作
  195. AVLTreeNode * doubleRotateLR(AVLTreeNode *node) {
  196. node->lchild = leftRotateRR(node->lchild);
  197. return rightRotateLL(node);
  198. };
  199. //对于RL类型,先对右子树进行一次LL类型的右旋转操作,然后再对根节点进行一次RR类型的左旋转操作
  200. AVLTreeNode * doubleRotateRL(AVLTreeNode *node) {
  201. node->rchild = rightRotateLL(node->rchild);
  202. return leftRotateRR(node);
  203. };
  204. //平衡LL或LR类型
  205. void balanceLeft(AVLTreeNode **node, int key) {
  206. AVLTreeNode *p = *node;
  207. if (getHeight(p->lchild) - getHeight(p->rchild) > 1) {
  208. if (key < p->lchild->data) { //LL
  209. *node = rightRotateLL(p);
  210. } else { //LR
  211. *node = doubleRotateLR(p);
  212. }
  213. }
  214. }
  215. //平衡RR或RL类型
  216. void balanceRight(AVLTreeNode **node, int key) {
  217. AVLTreeNode *p = *node;
  218. if (getHeight(p->rchild) - getHeight(p->lchild) > 1) {
  219. if (key > p->rchild->data) { //RR
  220. *node = leftRotateRR(p);
  221. } else { //RL
  222. *node = doubleRotateRL(p);
  223. }
  224. }
  225. }

发表评论

表情:
评论列表 (有 0 条评论,237人围观)

还没有评论,来说两句吧...

相关阅读

    相关 数据结构算法简记AVL

    前面记录了二叉查找树,它在搜索方面的效率显而易见,可它也存在某种缺陷,假设我们连续插入较小或较大的数据,那么二叉查找树将会逐渐退变为一个线性结构,从而搜索就变为了线性查找,效率