NDK-045: AVL树

先左旋再右旋.png
右旋.png
左旋.png
先右旋在左旋.png

AVL树

一种自平衡的二叉搜索树.AVL树通过在每个节点上维护一个平衡因子来确保树的平衡,平衡因子定义为左子树和右子树的高度差。
AVL树的性质要求任何节点的左右子树的高度差绝对值不超过1,这使得AVL树在插入和删除操作后能够通过旋转操作来保持平衡。
AVL 是大学教授 G.M. Adelson-Velsky 和 E.M. Landis 名称的缩写,他们提出的平衡二叉树的概念,为了纪念他们,将 平衡二叉树 称为 AVL树。

AVL树的主要特点包括:

  • 自平衡性:AVL树通过旋转操作来保持平衡,确保树的左右子树高度差不超过1。
  • 二叉搜索树:AVL树本质上是一棵二叉搜索树,其节点值满足左子节点小于父节点,右子节点大于父节点的规则。
  • 高度平衡:AVL树的每个节点的左右子树高度差的最大值为1,因此也被称为高度平衡树。

1.为什么引入AVL树(平衡二叉搜索树)。平衡二叉排序树。插入数据导致不平衡时,需要旋转。

二叉搜索树,致命的弱点,接近排好序的数据,查找时会退化成 0(N),相当于数组。AVL树能解决这个问题。
找资料(google)
左旋,右旋,先左旋后右旋,先右旋后左旋
AVL树 的定义:

  1. 左右两个子树的高度差,不会超过1;
  2. 空树也是平衡二叉树,每棵子树也是平衡二叉树。

平衡二叉搜索树的意义:普通的二叉搜索树,会出现极度不平衡的情况,复杂度有可能会退化成O(N), 所以引入AVL树。
平衡二叉搜索树,可以确保时间复杂度在O(logN)

2.旋转操作: 谁不平衡,旋转谁?

原数据:[3,2,1,4,5,6,7, 10,9,8]

发现如果打破了平衡的规则,就对其进行调整,使其满足一棵平衡二叉树。
右旋:3,2,1 把2提上来作为根节点,1不动 左边孩子,3作为2的右孩子。变成了平衡二叉树。3不平衡了,将3 往右边旋转了。
再插入4,不要调整;
左旋:再插入5,把4提上来变成根节点2的右孩子,原来的节点3作为4的左孩子,右边5保持不变。3不平衡了,将3往左边旋转了(作为4的左孩子)。
左旋:再插入6,此时对于2不平衡了,把4作为根节点,将2左旋(2不平衡了)。
【2进行 左旋,被提上去的节点4 左孩子非空,要作为原根节点的右孩子】
原来4节点的左孩子3,作为2的右孩子。
再插入7,在6的右节点位置,此时5不平衡了(左旋),6的左孩子5,右孩子7.
再插入10,在7的右节点位置,此时还是平衡的。
再插入9,在10的左边位置,此时不平衡了。此时7不平衡了。把10作为根。单纯左旋不满足需求,快要【先右旋10,再左旋7】.
左边的高度,大于右边的高度。
再插入8, 插入7的右节点位置,此时7/10/9都是平衡的。但是6是不平衡的。所以要旋转6.
单纯的左旋6,9变成根节点,左孩子6/右孩子10;6的左孩子5/右孩子7;7的右孩子8. 还是不平衡的。
因为9的左孩子,比右孩子的高度 高。
先对9右旋,7变成根节点。7的右节点 变成9,9的左节点8/右节点10.
此时对6还是不平衡,把6再左旋转一把,7成根节点,7的左孩子6, 6的左孩子5. 进而成平衡二叉树。


左旋的过程:加入9 的时候。7变得 不平衡了。

  1. 把(7) 的右孩子(10) 作为它的 根节点。
  2. 把根(7) 作为了现在根(10) 的 左孩子(7)。
  3. 如果原来的右孩子 的 左孩子 不为空(9),就把这个左孩子作为 现在左孩子(7) 的 右孩子(9)。
    左旋 特殊情况:左旋转完之后,发现(10)还是不平衡。一次左旋不能满足情况。
    所以要先右旋,再左旋。

先右旋 再左旋。【左边的高度 大于 右边的高度】
先把节点(10)右旋,变成7的右节点9,9的右节点10. 此时是还是不平衡,
左旋(7不平衡,左旋7),变成9根节点 提升,左节点7,右节点10.
先左旋 再右旋。【右边的高度3,大于左边的1】

修正一棵平衡二叉树:

左旋、右旋、先右旋再左旋、先左旋再右旋。 ---->红黑树。

【先左旋再右旋】eg: 3,-1(左子树),0(-1的右边) 的二叉树,单纯的右旋不行。
先左旋,变成3,左0, 左-1;
再右旋,变成0,左-1/右3.

优化app卡顿:
TraceView 找到卡顿的地方,二维带权的正泰分布函数(高斯函数)。
图片越大,算法复杂度也越高。

3.插入调整,增加的过程中不断地调整。

在新增的过程中,不断地进行调整。发现打破了平衡规则,就对其进行调整,使其满足平衡二叉树。

template<class K, class V>
struct TreeNode {
public:
    TreeNode<K, V> *left = NULL;
    TreeNode<K, V> *right = NULL;
    K key;
    V value;
    int height;// 当前树的高度

    TreeNode(K key, V value) : height(1) {// 初始节点的高度为 1
        this->right = NULL;
        this->left = NULL;
        this->key = key;
        this->value = value;
    }

    TreeNode(TreeNode<K, V> *pNode) : height(pNode->height) {
        this->left = pNode->left;
        this->right = pNode->right;
        this->key = pNode->key;
        this->value = pNode->value;
    }
};


    int getHeight(TreeNode<K, V> *pNode) {
        return pNode ? pNode->height : 0;
    }

    // 四个旋转操作
    // 右旋:pNode自己变成右孩子,原来的左孩子变成新根节点,原左孩子的右孩子 变成新右孩子的左孩子。
    TreeNode<K, V> *R_Rotation(TreeNode<K, V> *pNode) {
         TreeNode<K, V> *left = pNode->left; // 1.原来的左孩子作为根节点。用作返回
        pNode->left = left->right;  // 2.左节点的右孩子,将来作为原根节点的左孩子。
        left->right = pNode;        // 3.原来的根节点,作为新根节点的右孩子。

        // 更新高度= 左边高度/右边高度最大值 +1.
        pNode->height = max(getHeight(pNode->left), getHeight(pNode->right)) + 1;
        left->height = max(getHeight(left->left), getHeight(left->right)) + 1;
        return left; // 4.返回原根节点的左孩子,作为新根节点。
    }

    // 左旋操作
    TreeNode<K, V> *L_Rotation(TreeNode<K, V> *pNode) {
        TreeNode<K, V> *right = pNode->right; // 1.原节点的左节点作为根节点。用作返回
        pNode->right = right->left;     // 2.右节点的左孩子,将要作为原根节点的右孩子。
        right->left = pNode;            // 3.原节点,作为新节点的左孩子。

        //  更新高度 
        pNode->height = max(getHeight(pNode->left), getHeight(pNode->right)) + 1;
        right->height = max(getHeight(right->left), getHeight(right->right)) + 1;
        return right; // 4.返回pNode的右节点right为 新根节点。
    }


    // 返回一个节点 (逻辑问题? 1 ,2) ,多去理解
    TreeNode<K, V> *addNode(TreeNode<K, V> *pNode, K key, V value) {
        if (pNode == NULL) {
            count++;
            return new TreeNode<K, V>(key, value);
        }

        if (pNode->key > key) {// 小
            pNode->left = addNode(pNode->left, key, value); // 往左边添加节点
            if (getHeight(pNode->left) - getHeight(pNode->right) == 2) {
                // 调整 , 不能简简单单的就做一次右旋。还会出现要先左 再右旋的情况。
                pNode = R_Rotation(pNode);
            }
        } else if (pNode->key < key) {
            pNode->right = addNode(pNode->right, key, value); // 往右边添加节点
            if (getHeight(pNode->right) - getHeight(pNode->left) == 2) {
                // 调整(要考虑先右再左旋)
                pNode = L_Rotation(pNode);
            }
        } else {
            pNode->value = value;
        }

        // 更新二叉树的高度
        pNode->height = max(getHeight(pNode->left), getHeight(pNode->right)) + 1;
        return pNode;
    }

4.删除调整--很复杂

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 217,826评论 6 506
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,968评论 3 395
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 164,234评论 0 354
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,562评论 1 293
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,611评论 6 392
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,482评论 1 302
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,271评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,166评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,608评论 1 314
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,814评论 3 336
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,926评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,644评论 5 346
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,249评论 3 329
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,866评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,991评论 1 269
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 48,063评论 3 370
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,871评论 2 354

推荐阅读更多精彩内容