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

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

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

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

首先是创建AVL树:

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

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

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

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

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

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

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

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

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

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

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

JS描述:

//AVL树节点结构
function AVLTreeNode(data) {
  this.data = data;
  this.leftChild = null;
  this.rightChild = null;
  this.height = 0;
}

var AVLTreeUtil = {
  //由排序数组创建AVL树
  createAVLTree: function(array, low, high) {
    if (high < low) return null;

    var mid = Math.floor((low + high) / 2);

    var newNode = new AVLTreeNode(array[mid]);

    newNode.leftChild = this.createAVLTree(array, low, mid - 1);
    newNode.rightChild = this.createAVLTree(array, mid + 1, high);

    this.updateHeight(newNode);

    return newNode;
  },
  //前序遍历子树
  preOrderTraverse: function(node, visitFn) {
    if (node) {
      visitFn(node);

      this.preOrderTraverse(node.leftChild, visitFn);
      this.preOrderTraverse(node.rightChild, visitFn);
    }
  },
  //中序遍历子树
  inOrderTraverse: function(node, visitFn) {
    if (node) {
      this.inOrderTraverse(node.leftChild, visitFn);

      visitFn(node);

      this.inOrderTraverse(node.rightChild, visitFn);
    }
  },
  //插入节点并保持树平衡
  insertNodeWithBalance: function(node, key) {
    if (!node) {
      return new AVLTreeNode(key);
    }

    if (key < node.data) {
      node.leftChild = this.insertNodeWithBalance(node.leftChild, key);

      //平衡当前节点和左子树
      node = this.balanceLeft(node, key);
    } else {
      node.rightChild = this.insertNodeWithBalance(node.rightChild, key);

      //平衡当前节点和右子树
      node = this.balanceRight(node, key);
    }

    this.updateHeight(node);

    return node;
  },
  //移除子树中指定值的节点
  removeNodeWithBalance: function(node, key) {
    if (!node) return null;

    if (key < node.data) {
      node.leftChild = this.removeNodeWithBalance(node.leftChild, key);

      //平衡当前节点和左子树
      node = this.balanceLeft(node, key);

      this.updateHeight(node);

      return node;
    }

    if (key > node.data) {
      node.rightChild = this.removeNodeWithBalance(node.rightChild, key);

      //平衡当前节点和右子树
      node = this.balanceRight(node, key);

      this.updateHeight(node);

      return node;
    }

    //叶子节点
    if (!node.leftChild && !node.rightChild) {
      return null;
    }

    //仅左子树不存在,但存在右子树
    if (!node.leftChild) {
      return node.rightChild;
    }

    //仅右子树不存在,但存在左子树
    if (!node.rightChild) {
      return node.leftChild;
    }

    //下面处理左右子树均不为空的情况

    //查找右子树中最小节点
    var minNode = this.findMinNode(node.rightChild);

    //替换
    node.data = minNode.data;

    //在右子树中移除最小节点
    node.rightChild = this.removeNodeWithBalance(node.rightChild, minNode.data);

    //平衡当前节点和右子树
    node = this.balanceRight(node, key);

    this.updateHeight(node);

    return node;
  },
  //查找指定子树中最小节点
  findMinNode: function(node) {
    while (node && node.leftChild) {
      node = node.leftChild;
    }

    return node;
  },
  //获取节点树高
  getHeight: function(node) {
    if (!node) return -1;

    return node.height;
  },
  //更新节点树高
  updateHeight: function(node) {
    node.height = Math.max(this.getHeight(node.leftChild), this.getHeight(node.rightChild)) + 1;
  },
  //LL型右旋操作
  rightRotateLL: function(node) {
    var pivot = node.leftChild;

    node.leftChild = pivot.rightChild;

    pivot.rightChild = node;

    this.updateHeight(node);
    this.updateHeight(pivot);

    return pivot;
  },
  //RR型左旋操作
  leftRotateRR: function(node) {
    var pivot = node.rightChild;

    node.rightChild = pivot.leftChild;

    pivot.leftChild = node;

    this.updateHeight(node);
    this.updateHeight(pivot);

    return pivot;
  },
  //LR型双旋操作
  doubleRotateLR: function(node) {
    node.leftChild = this.leftRotateRR(node.leftChild);

    return this.rightRotateLL(node);
  },
  //RL型双旋操作
  doubleRotateRL: function(node) {
    node.rightChild = this.rightRotateLL(node.rightChild);

    return this.leftRotateRR(node);
  },
  //平衡左边
  balanceLeft: function(node, key) {
    if (this.lostBalance(node)) {
      if (key < node.leftChild.data) {
        node = this.rightRotateLL(node);  //LL
      } else {
        node = this.doubleRotateLR(node); //LR
      }
    }

    return node;
  },
  //平衡右边
  balanceRight: function(node, key) {
    if (this.lostBalance(node)) {
      if (key > node.rightChild.data) {
        node = this.leftRotateRR(node);   //RR
      } else {
        node = this.doubleRotateRL(node); //RL
      }
    }

    return node;
  },
  //是否失去平衡
  lostBalance: function(node) {
    //左右树高之差大于1就失去了平衡
    return Math.abs(this.getHeight(node.leftChild) - this.getHeight(node.rightChild)) > 1;
  }
};

//已排序数据
var array = [1, 3, 4, 6, 7, 8, 10, 13, 15];

var rootNode = AVLTreeUtil.createAVLTree(array, 0, array.length - 1);

//用于存放节点遍历序列
var orderArray = [];

var visitFn = function(node) {
  orderArray.push(node.data);
};

var printInAndPre = function() {
  orderArray.length = 0;

  AVLTreeUtil.inOrderTraverse(rootNode, visitFn);
  console.log(‘in order:‘, orderArray.join(‘ ‘));

  orderArray.length = 0;

  AVLTreeUtil.preOrderTraverse(rootNode, visitFn);
  console.log(‘pre order:‘, orderArray.join(‘ ‘));
};

console.log(‘--- after init ---‘);
printInAndPre();

//插入节点14并使树平衡
rootNode = AVLTreeUtil.insertNodeWithBalance(rootNode, 14);

console.log(‘--- after 14 inserted ---‘);
printInAndPre();

rootNode = AVLTreeUtil.removeNodeWithBalance(rootNode, 14);

console.log(‘--- after 14 removed ---‘);
printInAndPre();

rootNode = AVLTreeUtil.insertNodeWithBalance(rootNode, 0);
rootNode = AVLTreeUtil.insertNodeWithBalance(rootNode, -1);

console.log(‘--- after 0 and -1 inserted ---‘);
printInAndPre();

C语言描述:


#include <stdio.h>
#include <stdlib.h>

#define max(a,b) (a > b ? a : b)

//AVL树节点结构体
typedef struct node {
    int data;
    struct node *lchild, *rchild;
    int height;
} AVLTreeNode;

AVLTreeNode * createAVLTree(int *array, int low, int high);

void preOrderTraverse(AVLTreeNode *node);
void inOrderTraverse(AVLTreeNode *node);

AVLTreeNode * findMinNode(AVLTreeNode *node);

int getHeight(AVLTreeNode *node);
void updateHeight(AVLTreeNode *node);

AVLTreeNode * insertNodeWithBalance(AVLTreeNode *node, int key);
AVLTreeNode * removeNodeWithBalance(AVLTreeNode *node, int key);

AVLTreeNode * rightRotateLL(AVLTreeNode *node);
AVLTreeNode * doubleRotateLR(AVLTreeNode *node);
AVLTreeNode * leftRotateRR(AVLTreeNode *node);
AVLTreeNode * doubleRotateRL(AVLTreeNode *node);

void balanceLeft(AVLTreeNode **node, int key);
void balanceRight(AVLTreeNode **node, int key);

int main(int argc, const char * argv[]) {

    //已排序数据
    int array[] = {1, 3, 4, 6, 7, 8, 10, 13, 15};
    int size = 9;

    AVLTreeNode *rootNode = createAVLTree(array, 0, size - 1);

    printf("---after init ---\nin: ");

    inOrderTraverse(rootNode);

    printf("\npre: ");
    preOrderTraverse(rootNode);

    rootNode = insertNodeWithBalance(rootNode, 14);

    printf("\n--- after 14 inserted ---\nin: ");
    inOrderTraverse(rootNode);

    printf("\npre: ");
    preOrderTraverse(rootNode);

    rootNode = removeNodeWithBalance(rootNode, 14);

    printf("\n--- after 14 removed ---\nin: ");
    inOrderTraverse(rootNode);

    printf("\npre: ");
    preOrderTraverse(rootNode);

    rootNode = insertNodeWithBalance(rootNode, 0);
    rootNode = insertNodeWithBalance(rootNode, -1);

    printf("\n--- after 14 removed ---\nin: ");
    inOrderTraverse(rootNode);

    printf("\npre: ");
    preOrderTraverse(rootNode);

    return 0;
}

//初始化AVL树
AVLTreeNode * createAVLTree(int *array, int low, int high) {
    if(high < low) return NULL;

    //取出子树根节点值
    int mid = (low + high) / 2;

    //创建子树根节点
    AVLTreeNode *newNode = (AVLTreeNode *) malloc(sizeof(AVLTreeNode));
    newNode->data = array[mid];
    newNode->height = 0;

    //创建左右子树
    newNode->lchild = createAVLTree(array, low, mid - 1);
    newNode->rchild = createAVLTree(array, mid + 1, high);

    //更新根节点树高
    updateHeight(newNode);

    return newNode;
}

//先序遍历,用来验证平衡后的二叉树
void preOrderTraverse(AVLTreeNode *node) {
    if (node != NULL) {
        printf("%d ", node->data);

        preOrderTraverse(node->lchild);

        preOrderTraverse(node->rchild);
    }
}

//中序遍历
void inOrderTraverse(AVLTreeNode *node) {
    if (node != NULL) {
        inOrderTraverse(node->lchild);

        printf("%d ", node->data);

        inOrderTraverse(node->rchild);
    }
}

//查找指定子树中最小节点
AVLTreeNode * findMinNode(AVLTreeNode *node) {

    while (node != NULL && node->lchild != NULL) {
        node = node->lchild;
    }

    return node;
}

//插入节点并保持平衡
AVLTreeNode * insertNodeWithBalance(AVLTreeNode *node, int key) {

    //新增叶子节点,并返回该节点,父节点会与其建立关联
    if (node == NULL) {
        node = (AVLTreeNode *) malloc(sizeof(AVLTreeNode));
        node->data = key;
        node->height = 0;
        node->lchild = node->rchild = NULL;

        return node;
    }

    if (key < node->data) {
        node->lchild = insertNodeWithBalance(node->lchild, key);

        //平衡当前节点和左子树
        balanceLeft(&node, key);

    } else {
        node->rchild = insertNodeWithBalance(node->rchild, key);

        //平衡当前节点和右子树
        balanceRight(&node, key);
    }

    updateHeight(node);

    return node;
}

//在子树中移除值为key的节点并保持平衡
AVLTreeNode * removeNodeWithBalance(AVLTreeNode *node, int key) {

    if (node == NULL) return NULL;

    //向左子树继续
    if (key < node->data) {
        node->lchild = removeNodeWithBalance(node->lchild, key);

        //平衡当前节点和左子树
        balanceLeft(&node, key);

        updateHeight(node);

        return node;
    }

    //向右子树继续
    if (key > node->data) {
        node->rchild = removeNodeWithBalance(node->rchild, key);

        //平衡当前节点和右子树
        balanceRight(&node, key);

        updateHeight(node);

        return node;
    }

    //以下是匹配到节点后的几个不同的情况

    //移除叶子节点
    if (node->lchild == NULL && node->rchild == NULL) {
        //释放
        free(node);

        return NULL;
    }

    //仅左子树不存在
    if (node->lchild == NULL) {
        AVLTreeNode *rchild = node->rchild;

        //释放
        free(node);

        return rchild;
    }

    //仅右子树不存在
    if (node->rchild == NULL) {
        AVLTreeNode *lchild = node->lchild;

        //释放
        free(node);

        return lchild;
    }

    //-----最后如果左右子节点均存在,则先找到右子树最小子节点,替换其值,然后将最小节点删除-----

    //查找右子树最小节点
    AVLTreeNode *minNode = findMinNode(node->rchild);

    //替换其值
    node->data = minNode->data;

    //在右子树中移除最小节点
    node->rchild = removeNodeWithBalance(node->rchild, minNode->data);

    //平衡当前节点和右子树
    balanceRight(&node, key);

    updateHeight(node);

    return node;
}

//获取子树根节点高度
int getHeight(AVLTreeNode *node) {
    if (node == NULL) return -1;

    return node->height;
}

//更新节点树高
void updateHeight(AVLTreeNode *node) {
    node->height = max(getHeight(node->lchild), getHeight(node->rchild)) + 1;
}

//对LL类型做右旋转操作
AVLTreeNode * rightRotateLL(AVLTreeNode *node) {

    //获取左子节点作为轴点
    AVLTreeNode *pivot = node->lchild;

    node->lchild = pivot->rchild;

    pivot->rchild = node;

    updateHeight(node);
    updateHeight(pivot);

    return pivot;
};

//对RR类型做左旋转操作
AVLTreeNode * leftRotateRR(AVLTreeNode *node) {

    //获取右子节点作为轴点
    AVLTreeNode *pivot = node->rchild;

    node->rchild = pivot->lchild;

    pivot->lchild = node;

    updateHeight(node);
    updateHeight(pivot);

    return pivot;
};

//对于LR类型,先对左子节点进行一次RR类型的左旋转操作,然后再对根节点进行一次LL类型的右旋转操作
AVLTreeNode * doubleRotateLR(AVLTreeNode *node) {

    node->lchild = leftRotateRR(node->lchild);

    return rightRotateLL(node);
};

//对于RL类型,先对右子树进行一次LL类型的右旋转操作,然后再对根节点进行一次RR类型的左旋转操作
AVLTreeNode * doubleRotateRL(AVLTreeNode *node) {

    node->rchild = rightRotateLL(node->rchild);

    return leftRotateRR(node);
};

//平衡LL或LR类型
void balanceLeft(AVLTreeNode **node, int key) {

    AVLTreeNode *p = *node;

    if (getHeight(p->lchild) - getHeight(p->rchild) > 1) {
        if (key < p->lchild->data) {    //LL
            *node = rightRotateLL(p);
        } else {                        //LR
            *node = doubleRotateLR(p);
        }
    }
}

//平衡RR或RL类型
void balanceRight(AVLTreeNode **node, int key) {

    AVLTreeNode *p = *node;

    if (getHeight(p->rchild) - getHeight(p->lchild) > 1) {
        if (key > p->rchild->data) {    //RR
            *node = leftRotateRR(p);
        } else {                        //RL
            *node = doubleRotateRL(p);
        }
    }
}
时间: 2024-10-14 23:52:19

数据结构与算法简记:AVL树的相关文章

python数据结构与算法 38 分析树

分析树 树的结构完成以后,该是时候看看它能做点什么实事儿了.这一节里,我们研究一下分析树.分析树能够用于真实世界的结构表示,象语法或数学表达式一类的. 图1 一个简单语句的分析树 图1所示是一个简单语句的层级结构,把语句表示为树结构可以让我们用子树来分析句子的组成部分. 图2 ((7+3)?(5?2))的分析树 我们也可以把数学表达式如((7+3)?(5?2))表示为分析树,如图2.此前我们研究过完全括号表达式,这个表达式表达了什么呢?我们知道乘法的优先级比加减要高,但因为括号的关系,在做乘法之

自己动手实现java数据结构(七) AVL树

1.AVL树介绍 前面我们已经介绍了二叉搜索树.普通的二叉搜索树在插入.删除数据时可能使得全树的数据分布不平衡,退化,导致二叉搜索树最关键的查询效率急剧降低.这也引出了平衡二叉搜索树的概念,平衡二叉搜索树在此前的基础上,通过一系列的等价变换使二叉搜索树得以始终处于"平衡"的状态,拥有稳定且高效的查询效率. AVL树是最早被计算机科学家发明的自平衡二叉搜索树,AVL树得名于它的发明者G. M. Adelson-Velsky和E. M. Landis,他们在1962年的论文<An a

数据结构与算法简记--剖析微服务接口鉴权限流背后的数据结构和算法

微服务鉴权限流剖析 微服务 把复杂的大应用,解耦拆分成几个小的应用. 有利于团队组织架构的拆分,毕竟团队越大协作的难度越大: 每个应用都可以独立运维,独立扩容,独立上线,各个应用之间互不影响. 有利就有弊: 大应用拆分成微服务之后,服务之间的调用关系变得更复杂,平台的整体复杂熵升高,出错的概率.debug 问题的难度都高了好几个数量级. 为了解决这些问题,服务治理便成了微服务的一个技术重点. 服务治理 简单点讲,就是管理微服务,保证平台整体正常.平稳地运行. 涉及的内容:鉴权.限流.降级.熔断.

数据结构与算法问题 AVL二叉平衡树

AVL树本质上还是一棵二叉搜索树,它的特点是: 本身首先是一棵二叉搜索树. 带有平衡条件:每个结点的左右子树的高度之差的绝对值(平衡因子)最多为1 #include <iostream> using namespace std; const int LH = 1; const int EH = 0; const int RH = -1; bool TRUE = 1; bool FALSE = 0; typedef struct BSTNode { int key; int bf; BSTNod

08-看图理解数据结构与算法系列(2-3树)

2-3树 2-3树,是最简单的B-树,其中2.3主要体现在每个非叶子节点都有2个或3个子节点,B-树即是平衡树,平衡树是为了解决不平衡树查询效率问题,常见的二叉平衡书有AVL树,它虽然提高了查询效率,但是插入操作效率不高,因为它需要再每次插入节点后维护树的平衡,而为了解决查询效率同时有兼顾插入效率,于是提出了2-3树. 2-3树特点 2-3树是一棵平衡树,但不是二叉平衡树. 对于高度相同的2-3树和二叉树,2-3树的节点数要大于满二叉树,因为有些节点可能有三个子节点. 2-3树可以是一棵空树.

数据结构与算法简记--红黑树

红黑树 平衡二叉树 定义:二叉树中任意一个节点的左右子树的高度相差不能大于 1. 完全二叉树.满二叉树其实都是平衡二叉树,非完全二叉树也有可能是平衡二叉树. 平衡二叉查找树 任何节点的左右子树高度相差不超过 1,是一种高度平衡的二叉查找树. 符合二叉查找树的特点:左子节点小于父节点,右子节点大于父节点. 发明的初衷是:解决普通二叉查找树在频繁的插入.删除等动态更新的情况下,出现时间复杂度退化的问题. “平衡”的意思,其实就是让整棵树左右看起来比较“对称”.比较“平衡”,不要出现左子树很高.右子树

数据结构与算法17—B树(B、B+、B*)

B树 B-树,就是B树,B树的原英文名是B-tree,所以很多翻译为B-树,就会很多人误以为B-树是一种树.B树是另外一种树.其实,B-tree就是B树. B-树的定义 B树(B-tree)是一种树状数据结构,是一种平衡的多路查找树,能够用来存储排序后的数据.这种数据结构能够让查找数据.循序存取.插入数据及删除的动作,都在对数时间内完成.B树,概括来说是一个一般化的二叉查找树,可以拥有多于2个子节点.与自平衡二叉查找树不同,B-树为系统最优化大块数据的读和写操作.B-tree算法减少定位记录时所

【数据结构和算法】:树和二叉树定义、基本术语和性质

树的定义和基本术语 ?树:是一类重要的非线性数据结构,是以分支关系定义的层次结构. ?根:树(tree)是n(n>=0)个结点的有限集T,对于非空树,其中有且仅有一个特定的结点,称为树的根(root). ?子树:当n>1时,其余结点可分为m(m>0)个互不相交的有限集T1,T2,--Tm,其中每一个集合本身又是一棵树,称为根的子树(subtree).每棵子树的根结点有且仅有一个直接前驱,但可以有0个或多个直接后继. 结点:包含一个数据元素及若干指向子树的分支. 结点的度:结点子树的个数.

09-看图理解数据结构与算法系列(B树)

B树 B树即平衡查找树,一般理解为平衡多路查找树,也称为B-树.B_树.是一种自平衡树状数据结构,能对存储的数据进行O(log n)的时间复杂度进行查找.插入和删除.B树一般较多用在存储系统上,比如数据库或文件系统. B树特点 B树可以定义一个m值作为预定范围,即m路(阶)B树. 每个节点最多有m个孩子. 每个节点至少有ceil(m/2)个孩子,除了根节点和叶子节点外. 对于根节点,子树个数范围为[2,m],节点内值的个数范围为[1,m-1]. 对于非根节点,节点内的值个数范围为[ceil(m/