博客
关于我
【用梨泰院class中的财阀世家带你洞悉替罪羊树】Scapegoat Tree原理,模板,例题
阅读量:294 次
发布时间:2019-03-03

本文共 2974 字,大约阅读时间需要 9 分钟。

重新理解替罪羊树

前言

我最近在学习数据结构时,逐渐对平衡树的概念产生了浓厚兴趣。通过对比和实践,我逐渐理解了替罪羊树的工作原理。这让我联想到韩剧中的财阀世家,复杂的权力斗争和权力更迭让我对替罪羊树的“拍扁重构”机制有了更深入的理解。

替罪羊树的概念

替罪羊树是一种基于“拍扁重构”的二叉平衡树,通过打乱子树的结构来维护平衡性。与旋转平衡树相比,替罪羊树更注重通过稳固的“亲情”来维持树的平衡。

核心原理

替罪羊树的平衡机制依赖于一个叫做“平衡因子”的概念。这个因子用于判断树的子树是否需要重构。具体来说,当左或右子树的节点数占比超过平衡因子的限制时,整个子树就会被拍扁重构。

平衡因子的选择

平衡因子的取值范围通常在0.5到1之间。常见的选择是0.7左右。这一参数值的选择需要权衡插入和查询的效率。较大的α值会减少重构次数,提高插入效率,但可能导致查询效率下降。

替罪羊树的优点与缺点

优点

  • 代码简洁:替罪羊树的代码逻辑相对简单,易于调试。
  • 性能较好:在大多数操作下,替罪羊树的性能表现优于一些旋转平衡树。
  • 易于理解:其“拍扁重构”机制直观地类似于对家族权力结构的调整。
  • 缺点

  • 区间操作受限:替罪羊树不支持区间操作,这在某些应用场景下是个限制。
  • 重构频率高:在某些负载下,频繁的重构操作可能带来性能瓶颈。
  • 替罪羊树的实现

    数据结构定义

    struct Scapegoat {    bool exist;    int son[2], val, alive, total;};int idx1, idx2, flag, root, Size;int cur[MAXN], memory[MAXN];

    重构函数

    void flat(int rt) {    if (!rt) return;    flat(t[rt].son[0]);    if (t[rt].exist) cur[++idx1] = rt;    else memory[++idx2] = rt;    flat(t[rt].son[1]);}void build(int l, int r, int &rt) {    int mid = (l + r) >> 1;    rt = cur[mid];    if (l == r) {        t[rt].son[0] = t[rt].son[1] = 0;        t[rt].alive = t[rt].total = 1;        return;    }    if (l < mid) build(l, mid - 1, t[rt].son[0]);    else t[rt].son[0] = 0;    build(mid + 1, r, t[rt].son[1]);    t[rt].total = t[t[rt].son[0]].total + t[t[rt].son[1]].total + 1;    t[rt].alive = t[t[rt].son[0]].alive + t[t[rt].son[1]].alive + 1;}void rebuild(int &rt) {    idx1 = 0;    flat(rt);    if (idx1) build(1, idx1, rt);    else rt = 0;}

    插入函数

    void insert(int &rt, int val) {    if (!rt) {        rt = memory[idx2--];        t[rt].val = val;        t[rt].exist = t[rt].alive = t[rt].total = 1;        t[rt].son[0] = t[rt].son[1] = 0;        return;    }    t[rt].alive++;    t[rt].total++;    if (val <= t[rt].val) insert(t[rt].son[0], val);    else insert(t[rt].son[1], val);    if (Bad(rt)) rebuild(rt);}

    删除操作

    void Delete_rnk(int &rt, int rnk) {    if (t[rt].exist && t[t[rt].son[0]].alive + 1 == rnk) {        t[rt].exist = 0;        t[rt].alive--;        return;    }    t[rt].alive--;    if (rnk <= t[t[rt].son[0]].alive + t[rt].exist)        Delete_rnk(t[rt].son[0], rnk);    else        Delete_rnk(t[rt].son[1], rnk - t[t[rt].son[0]].alive - t[rt].exist);}void Delete_val(int val) {    Delete_rnk(root, FindRank(val));    if ((double)t[root].total * alpha > t[root].alive)        rebuild(root);}

    查找功能

    int FindRank(int k) {    int rt = root, ans = 1;    while (rt) {        if (k <= t[rt].val) rt = t[rt].son[0];        else {            ans += t[t[rt].son[0]].alive + t[rt].exist;            rt = t[rt].son[1];        }    }    return ans;}int FindKth(int k) {    int rt = root;    while (rt) {        if (t[rt].exist && t[t[rt].son[0]].alive + 1 == k)            return t[rt].val;        else if (k <= t[t[rt].son[0]].alive)            rt = t[rt].son[0];        else {            k -= (t[t[rt].son[0]].alive + t[rt].exist);            rt = t[rt].son[1];        }    }    return -1;}

    总结

    替罪羊树以其独特的“拍扁重构”机制,为平衡树提供了一种直观的理解方式。通过类比现实中的财阀世家权力斗争,我们可以更直观地感受到替罪羊树的工作原理。虽然其在某些场景下存在局限性,但其代码简洁、逻辑清晰,使其成为学习平衡树的一种不错的选择。

    转载地址:http://pxil.baihongyu.com/

    你可能感兴趣的文章
    nodejs系列之express
    查看>>
    nodejs系列之Koa2
    查看>>
    Nodejs连接mysql
    查看>>
    nodejs连接mysql
    查看>>
    NodeJs连接Oracle数据库
    查看>>
    nodejs配置express服务器,运行自动打开浏览器
    查看>>
    NodeMCU教程 http请求获取Json中文乱码解决方案
    查看>>
    Nodemon 深入解析与使用
    查看>>
    NodeSession:高效且灵活的Node.js会话管理工具
    查看>>
    node~ http缓存
    查看>>
    node不是内部命令时配置node环境变量
    查看>>
    node中fs模块之文件操作
    查看>>
    Node中同步与异步的方式读取文件
    查看>>
    node中的get请求和post请求的不同操作【node学习第五篇】
    查看>>
    Node中的Http模块和Url模块的使用
    查看>>
    Node中自启动工具supervisor的使用
    查看>>
    Node入门之创建第一个HelloNode
    查看>>
    node全局对象 文件系统
    查看>>
    Node出错导致运行崩溃的解决方案
    查看>>
    Node响应中文时解决乱码问题
    查看>>