首页 > 其他分享 >平衡树

平衡树

时间:2023-10-11 19:22:47浏览次数:23  
标签:Rt cnt ch int splay fa 平衡

平衡树真的恶心死了!!!!!!好烦啊,又臭又长。

有很多种平衡树,替罪羊, treap,fhq, slpay。这里就说 splay, 和 bst 和 替罪羊 了,因为其他我都不会(悲


先说二叉排序树(二叉搜索树), 他的关系就是 左子树所有节点 < 根节点 < 右子树所有节点。也就是说,按照中序遍历可以找到有序序列。

这个时候我们就可以增删改(删了再加入)查了!

查找 通过搜索,发现如果这个节点等于自己,cnt++, 小于自己往左搜索,大于自己往右边搜索

比如说我们要增加,搜索,如果发现没有,就新增节点。

删除, 找到位置(同上)cnt--。

这个时候就发现一个很严重的问题,它的复杂度是树高,就有可能变成一条链,复杂度瞬间变成 \(O(n)\) (泰裤辣)。

平衡树要来了!

平衡树还有几个功能

  1. 要求排名
  2. 按照排名查数
  3. 前驱后继

替罪羊的思路就此诞生。 如果发现不平衡,就重构。

平衡的定义是非0节点占全部的 M% 以内,且左节点的个数在 右节点的 M% 一内。

发现不平衡后就把它变成线性的,再按照贪心思路,每次折半重构。

M 在 70 ~ 75 为最佳

代码就不贴了,因为很丑。


接下来就是 splay 了

splay的主要思路就是操作的数都要旋转到树顶端

这个时候就要提到左旋和右旋了。其实就是传统的左旋和右旋,还是讲一下吧。

如图是左旋,右旋就是反过来,将x往上面旋,只需要看x是左儿子,还是右儿子就可以了。

然而,无脑的旋转会被善良的出题人卡掉。

所以,我们就要牵扯到祖宗——爸爸的爸爸叫什么(

两种情况,第一种,如下图

先旋转自己,再旋转自己。

第二种,如下图

先转父亲,再转自己。

然后就是紧张刺激的写代码环节了!

bool check(int x) {
  return a[a[x].fa].ch[0] != x;
}

写一个函数判断是做儿子(0)还是右儿子(1)。

void push_up(int rt) {
  a[rt].size = a[a[rt].ch[0]].size + a[a[rt].ch[1]].size + a[rt].cnt;
}

更新自己,记得加上自己的个数。

void Add(int x, int y, bool f) { 
  a[y].ch[f] = x;
  a[x].fa = y;
}

x接在y的(f ? "右儿子" : "左儿子")上。

正片,开始。


如果x是左儿子,就左旋,否则就是右旋,这样就可以简单轻松的完成自动的完成旋转了(dig, zig害人啊)

void rotate(int x) {
  int y = a[x].fa, z = a[y].fa, d = check(x), w = a[x].ch[d ^ 1];
  Add(w, y, d);
  Add(x, z, check(y));
  Add(y, x, d ^ 1);
  push_up(y), push_up(x);
}

单次旋转函数,将x向上旋转。

void splay(int x, int p = 0) {
  for (int f = a[x].fa; f = a[x].fa, f != p; rotate(x)) {
    if (a[f].fa != p) {
      if (check(f) == check(x)) {
        rotate(f);
      } else {
        rotate(x);
      }
    }
  }
  if (p == 0) Rt = x;
}

多次旋转函数,将 x 旋顶。

void find(int x) {
  int p = Rt;
  while (a[p].ch[x > a[p].v] && x != a[p].v) {
    p = a[p].ch[x > a[p].v];
  }
  splay(p);
}

查找并旋转到顶。

void insert(int x) {
  int p = Rt, fa = 0;
  while (p && x != a[p].v) {
    fa = p;
    p = a[p].ch[x > a[p].v];
  }
  if (p)
    a[p].cnt++;
  else {
    p = ++cnt;
    if (fa) a[fa].ch[x > a[fa].v] = p;
    a[p] = MakeSplay(x, fa);
  }
  splay(p);
}

找到,如果已经有了就cnt++,否则添加。

int pre_suc(int x, int f) {
  find(x);
  if (!f && a[Rt].v < x || f && a[Rt].v > x) return Rt;
  int p = a[Rt].ch[f];
  while (a[p].ch[f ^ 1]) {
    p = a[p].ch[f ^ 1];
  }
  return p;
}

前驱0, 后驱1。先旋转到顶,然后查找。

注意了,删除不一样

void remove(int x) {
  int last = pre_suc(x, 0), next = pre_suc(x, 1);
  splay(last), splay(next, last);
  int p = a[next].ch[0];
  if (a[p].cnt > 1) {
    a[p].cnt--;
    splay(p);
  } else {
    a[next].ch[0] = 0;
    push_up(next), push_up(last);
  }
}

先把x的前驱旋转到顶,再将后继旋到前驱的后面。那么后继的左儿子就是 x,而且 x 没有儿子。

int rank1(int x) {
  int p = Rt;
  while (1) {
    if (a[p].ch[0] && a[a[p].ch[0]].size >= x) {
      p = a[p].ch[0];
    } else if (x > a[a[p].ch[0]].size + a[p].cnt) {
      x -= a[a[p].ch[0]].size + a[p].cnt;
      p = a[p].ch[1];
    } else {
      return p;
    }
  }
}

查排名为x的数,不多解释。

总代码来咯。

等等等等,还要加入最大值和最小值哦,不然有可能会没有前驱和后继哦(亲身

#include <iostream>

using namespace std;

const int kMaxN = 1e5 + 5;

int Rt, cnt;

struct Splay {
  int v, fa, cnt, size, ch[2];
} a[kMaxN];

Splay MakeSplay(int v, int fa) {
  Splay P;
  P.v = v, P.fa = fa, P.cnt = P.size = 1, P.ch[0] = P.ch[1] = 0;
  return P;
}

bool check(int x) {
  return a[a[x].fa].ch[0] != x;
}

void push_up(int rt) {
  a[rt].size = a[a[rt].ch[0]].size + a[a[rt].ch[1]].size + a[rt].cnt;
}

void Add(int x, int y, bool f) {
  a[y].ch[f] = x;
  a[x].fa = y;
}

void rotate(int x) {
  int y = a[x].fa, z = a[y].fa, d = check(x), w = a[x].ch[d ^ 1];
  Add(w, y, d);
  Add(x, z, check(y));
  Add(y, x, d ^ 1);
  push_up(y), push_up(x);
}

void splay(int x, int p = 0) {
  for (int f = a[x].fa; f = a[x].fa, f != p; rotate(x)) {
    if (a[f].fa != p) {
      if (check(f) == check(x)) {
        rotate(f);
      } else {
        rotate(x);
      }
    }
  }
  if (p == 0) Rt = x;
}

void find(int x) {
  int p = Rt;
  while (a[p].ch[x > a[p].v] && x != a[p].v) {
    p = a[p].ch[x > a[p].v];
  }
  splay(p);
}

void insert(int x) {
  int p = Rt, fa = 0;
  while (p && x != a[p].v) {
    fa = p;
    p = a[p].ch[x > a[p].v];
  }
  if (p)
    a[p].cnt++;
  else {
    p = ++cnt;
    if (fa) a[fa].ch[x > a[fa].v] = p;
    a[p] = MakeSplay(x, fa);
  }
  splay(p);
}

int pre_suc(int x, int f) {
  find(x);
  if (!f && a[Rt].v < x || f && a[Rt].v > x) return Rt;
  int p = a[Rt].ch[f];
  while (a[p].ch[f ^ 1]) {
    p = a[p].ch[f ^ 1];
  }
  return p;
}

void remove(int x) {
  int last = pre_suc(x, 0), next = pre_suc(x, 1);
  splay(last), splay(next, last);
  int p = a[next].ch[0];
  if (a[p].cnt > 1) {
    a[p].cnt--;
    splay(p);
  } else {
    a[next].ch[0] = 0;
    push_up(next), push_up(last);
  }
}

int rank1(int x) {
  int p = Rt;
  while (1) {
    if (a[p].ch[0] && a[a[p].ch[0]].size >= x) {
      p = a[p].ch[0];
    } else if (x > a[a[p].ch[0]].size + a[p].cnt) {
      x -= a[a[p].ch[0]].size + a[p].cnt;
      p = a[p].ch[1];
    } else {
      return p;
    }
  }
}

int main() {
  insert(0x3f3f3f3f);
  insert(-0x3f3f3f3f);
  int n, op, x;
  for (cin >> n; n; n--) {
    cin >> op >> x;
    switch (op) {
      case 1:
        insert(x);
        break;
      case 2:
        remove(x);
        break;
      case 3:
        find(x), cout << a[a[Rt].ch[0]].size << '\n';
        break;
      case 4:
        cout << a[rank1(x + 1)].v << '\n';
        break;
      case 5:
        cout << a[pre_suc(x, 0)].v << '\n';
        break;
      case 6:
        cout << a[pre_suc(x, 1)].v << '\n';
        break;
    }
  }
  return 0;
}

标签:Rt,cnt,ch,int,splay,fa,平衡
From: https://www.cnblogs.com/JiCanDuck/p/17757962.html

相关文章

  • 深度学习中的样本不平衡问题
    1.什么是样本不平衡问题?所谓的类别不平衡问题指的是数据集中各个类别的样本数量极不均衡。以二分类问题为例,假设正类的样本数量远大于负类的样本数量,通常情况下把样本类别比例超过4:1(也有说3:1)的数据就可以称为不平衡数据。样本不平衡实际上是一种非常常见的现象。比如:在欺诈交易......
  • 化学反应原理 —— 平衡 | 浓缩版
    平衡浓缩版2023.10.10\(\it0\)\(\sfGeneral\Solution\)这一块需要建立一个体系,把所有的知识建立一个结构,把它们互相联系起来。\(\it0.1\)动力学与热力学从动力学的角度考虑问题,就是考虑反应的速率。\(v_正\)\(v_逆\)相等且不变时,反应达到平衡。影响速率的因素:内......
  • 三维模型3DTile格式轻量化的数据压缩与性能平衡关系分析
    三维模型3DTile格式轻量化的数据压缩与性能平衡关系分析 对于三维模型的3DTile格式轻量化处理,数据压缩和性能之间的平衡关系是一个重要的考虑因素。以下是这两者关系的详细分析:1、数据压缩与加载速度:显然,更高级别的压缩可以创造更小的文件大小,从而加快从服务器到客户端的传输......
  • 【学习笔记】(13) 平衡树——记住不的板子
    TreapSplay无旋Treap——fhqtreap简介就是没有旋转操作的Treap,一些性质什么的都跟Treap类似。算法介绍(1)merge(x,y)将两棵“有序”(x中元素的权值最大值小于y中元素权值最小值)的Treap合并成一棵。intch[N][2],sz[N],pri[N],val[N];//val为权值,pri为优先级,sz......
  • 处理不平衡数据的十大Python库
    数据不平衡是机器学习中一个常见的挑战,其中一个类的数量明显超过其他类,这可能导致有偏见的模型和较差的泛化。有各种Python库来帮助有效地处理不平衡数据。在本文中,我们将介绍用于处理机器学习中不平衡数据的十大Python库,并为每个库提供代码片段和解释。 https://avoid.overfi......
  • 平衡树模板
    Splay#definelchch[0]#definerchch[1]classSplay{private:structNode{intval,cnt,sz;Node*fa,*ch[2];inlinevoidpushup(){sz=cnt;if(lch)sz+=lch->sz;......
  • 代码随想录算法训练营day17 | ● 110.平衡二叉树 ● 257. 二叉树的所有路径 ● 404.
    110.平衡二叉树classSolution{public:intgetHeight(TreeNode*node){if(node==NULL)return0;intleftHeight=getHeight(node->left);if(leftHeight==-1)return-1;intrightHeigh......
  • 平衡二叉树的平衡机制
    1.什么是平衡二叉树,就是任意节点的左右子树的层数之差不超过1.前提它是一个二叉树。 2.一个平衡二叉树,在以下4种情况下,会破坏平衡(为啥要知道4种基本的情况,因为跟左旋和右旋的息息相关)。 2.1根节点--->左子树--->左子节点。增加节点操作。简称左左。 2.2根节点--->左子树-......
  • 2020-12-21-两轮平衡小车探索
    layout:posttitle:两轮平衡小车探索categories:日志tags:-开发-开发任务BGImage:'https://github.xutongxin.me/https://raw.githubusercontent.com/xutongxin1/PictureBed/master/img0/20201220234325.png'jekyll-theme-WuK:musicid:'744590......
  • leetcode 平衡二叉树
    给定一个二叉树,判断它是否是高度平衡的二叉树。本题中,一棵高度平衡二叉树定义为:一个二叉树每个节点的左右两个子树的高度差的绝对值不超过1。示例1:输入:root=[3,9,20,null,null,15,7]输出:true示例2:输入:root=[1,2,2,3,3,null,null,4,4]输出:false示例3:输入:root......