前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >c/c++补完计划(五): 平衡二叉树和二叉搜索树

c/c++补完计划(五): 平衡二叉树和二叉搜索树

作者头像
sean_yang
发布2020-08-11 11:15:54
4140
发布2020-08-11 11:15:54
举报
文章被收录于专栏:Sorrower的专栏

前言

来看维基的说明: AVL树:是最早被发明的自平衡二叉查找树。在AVL树中,任一节点对应的两棵子树的最大高度差为1,因此它也被称为高度平衡树。查找、插入和删除在平均和最坏情况下的时间复杂度都是

。增加和删除元素的操作则可能需要借由一次或多次树旋转,以实现树的重新平衡。 从查找树的角度来看, 还是非常实用的结构, 面试也很喜欢考, 我回想了一下, 在3家以上公司遇到了, 当然有一次是因为我不会红黑树, 被降级要求写AVL树, 是我不配(手动无奈).

平衡二叉树判断

自顶向下

思路是, 左右子树都要是平衡二叉树, 且左右子树的高度差小于2. 核心代码也很简单, 基本就是把思路用代码写出来.

代码语言:javascript
复制
bool isBalanced(TreeNode *root) {
    if (root == nullptr) {
        return true;
    }

    return isBalanced(root->left) && isBalanced(root->right) &&
           (abs(height(root->left) - height(root->right)) < 2);

}

然后就是高度的获取, 当前节点的高度, 就是, 左右子树的高度大的那个+1. 这里你可以用系统的max函数, 也可以自己写一个lambda, 建议自己写一个.

代码语言:javascript
复制
int height(TreeNode *node) {
    if (node == nullptr) {
        return -1;
    }

//    return max(height(node->left), height(node->right)) + 1;
    auto max = [](int left, int right) { return left < right ? right : left; };
    return max(height(node->left), height(node->right)) + 1;
}

放到力库跑一下, 看到效果还行.

image

自底向上

但是很明显有很多重复计算, 而且思路是自顶向下的. 那么考虑一个自底向上的. 也不用维护数据结构那么复杂, 考虑传入一个height引用.

代码语言:javascript
复制
bool isBalancedCore(TreeNode *root, int &h) {
    if (root == nullptr) {
        h = -1;
        return true;
    }

    int l, r;
    if (isBalancedCore(root->left, l) && isBalancedCore(root->right, r)
        && abs(l - r) < 2) {
        auto max = [=] { return l > r ? l : r; };
        h = max() + 1;
        return true;
    }
    return false;
}

bool isBalanced(TreeNode *root) {
    int height;
    return isBalancedCore(root, height);
}

思路是其实是后序遍历, 先判断左, 后判断右, 最后是当前节点, 也就是中. 当某次不满足时, 会直接返回false, 然后一路返回到顶, 所以复杂度肯定要优于之前的.

image

你可能会觉得有点别扭, 因为这里的true和false似乎可以用height搞定, 那为啥不简化一下.

代码语言:javascript
复制
int isBalancedHelper(TreeNode *root) {
    if (root == nullptr) {
        return 0;
    }

    int left = isBalancedHelper(root->left);
    if (left == -1) return -1;
    int right = isBalancedHelper(root->right);
    if (right == -1) return -1;

    auto max = [](int l, int r) { return l > r ? l : r; };
    return abs(right - left) < 2 ? max(left, right) + 1 : -1;
}

bool isBalanced(TreeNode *root) {
    return isBalancedHelper(root) != -1;
}

可以看到, 等于是把之前的与判断拆分了, 当返回-1, 就意味着当前子树不是平衡树, 然后一路返回.

image

二叉搜索树的最近公共祖先

这个题思路很重要, 不是难题, 一个暴力做法, 我直接保存两个查找的路径, 然后比对, 但是问题是什么?

  • 要维护一个数组记录路径
  • 没有利用起二叉搜索树的特性, 人家帮你弄好了左小右大的树, 你当一般树, 不是很搞笑吗?

那思路其实很简单, 当两个节点都小于当前节点, 说明还未分叉, 继续找; 如果一个大于当前节点, 一个小于当前节点, 就是终止条件了. 当然, 可以先排序两个节点, 这样判断就少了, 不用每次判断2个.

代码语言:javascript
复制
TreeNode *lowestCommonAncestor(TreeNode *root, TreeNode *p, TreeNode *q) {
    if (p->val > q->val) {
        auto tmp = p;
        p = q;
        q = tmp;
    }

    while (root) {
        if (root->val < p->val) {
            root = root->right;
        } else if (root->val > q->val) {
            root = root->left;
        } else {
            break;
        }
    }

    return root;
}

最后

这里没有说到AVL左旋和右旋的问题, 但是实际上不是很难, 还是要自己动动手, 才能记得住.

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 前言
  • 平衡二叉树判断
    • 自顶向下
      • 自底向上
      • 二叉搜索树的最近公共祖先
      • 最后
      领券
      问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档