博客
关于我
【用梨泰院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/

    你可能感兴趣的文章
    Node.js 实现类似于.php,.jsp的服务器页面技术,自动路由
    查看>>
    Node.js 异步模式浅析
    查看>>
    node.js 怎么新建一个站点端口
    查看>>
    Node.js 文件系统的各种用法和常见场景
    查看>>
    Node.js 的事件循环(Event Loop)详解
    查看>>
    node.js 简易聊天室
    查看>>
    Node.js 线程你理解的可能是错的
    查看>>
    Node.js 调用微信公众号 API 添加自定义菜单报错的解决方法
    查看>>
    node.js 配置首页打开页面
    查看>>
    node.js+react写的一个登录注册 demo测试
    查看>>
    Node.js中环境变量process.env详解
    查看>>
    Node.js之async_hooks
    查看>>
    Node.js卸载超详细步骤(附图文讲解)
    查看>>
    Node.js基于Express框架搭建一个简单的注册登录Web功能
    查看>>
    Node.js安装与配置指南:轻松启航您的JavaScript服务器之旅
    查看>>
    Node.js安装及环境配置之Windows篇
    查看>>
    Node.js安装和入门 - 2行代码让你能够启动一个Server
    查看>>
    node.js安装方法
    查看>>
    Node.js官网无法正常访问时安装NodeJS的方法
    查看>>
    Node.js的循环与异步问题
    查看>>