替罪羊树(重量平衡树)总结及板子

   没事干写个板子来玩一玩...平衡树的板子,上一篇写的 splay 的题解,这一篇来搞点别的.其实就我自己来说,并不太喜欢 splay ,各种旋转什么的,扭扭捏捏一点都不爽,那怎么办咧,于是学了这么个东西---替罪羊树,不平衡就重构嘛,简单粗暴,写起来也方便.

  替罪羊树的主要思想就是将不平衡的树压成一个序列,然后暴力重构成一颗平衡的树.

  这里的平衡指的是:对于某个 0.5<=alpha<=1 满足 size( lson(x) )<=alpha*size(x) 并且 size( rson(x) )<=alpha*size(x),即这个节点的两棵子树的 size 都不超过以该节点为根的子树的 size ,那么就称这个子树(或节点)是平衡的, alpha 最好不要选 0.5 ,容易T飞,一般选 0.75 就挺好的.

  至于复杂度,虽说是重构,但复杂度并不高,压扁和重建都是递归操作,也就是像线段树一样的 log 级别,由于平衡的限制,插入,删除,即查询等操作也会控制在一个较低的级别,均摊下来替罪羊树的总复杂度是 O(logn) 的.

  细节的地方我们以这道题为例:[BZOJ 3224]普通平衡树

  这应该算是一道平衡树板子题吧,操作也正好适用于替罪羊树(简单的 insert delete ,没有 reverse ).

  做题的时候就在想一个问题,为什么要叫替罪羊树呢,应该是和 delete 操作有关,替罪羊树中的删除操作,就是用被删除节点的左子树的最后一个节点或者右子树的第一个节点来顶替被删除节点的位置,即"替罪羊".其他操作在代码中体现.

  我觉得我的代码还是 蛮好看的 ,窃笑......

 

#include<iostream>
#include<cstdio>
#include<cstdlib>
#define inf (1<<30)
#define maxn (2100000)
#define db double
#define il inline
#define RG register
using namespace std;
il int gi(){ 
  RG int x=0,q=1; RG char ch=getchar(); 
  while( ( ch<'0' || ch>'9' ) && ch!='-' ) ch=getchar();
  if( ch=='-' ) q=-1,ch=getchar(); 
  while(ch>='0' && ch<='9') x=x*10+ch-48,ch=getchar(); 
  return q*x; 
}

const db al=0.75;
struct node{ int son[2],fa,size,num; }t[maxn];
int n,cnt,root;

il bool balance(RG int id){  //平衡限制,这里的 alpha 取0.75
  return (db)t[id].size*al>=(db)t[ t[id].son[0] ].size
    && (db) t[id].size*al>=(db)t[t[ id].son[1] ].size;
}

int cur[maxn],sum;

il void recycle(RG int id){   //压扁成一个序列,按大小顺序回收节点
  if(t[id].son[0]) recycle(t[id].son[0]);
  cur[++sum]=id;
  if(t[id].son[1]) recycle(t[id].son[1]);
}

il int build(RG int l,RG int r){  //递归建树
  if(l>r) return 0;
  RG int mid=(l+r)>>1,id=cur[mid];
  t[ t[id].son[0]=build(l,mid-1) ].fa=id;
  t[ t[id].son[1]=build(mid+1,r) ].fa=id;
  t[id].size=t[ t[id].son[0] ].size+t[ t[id].son[1] ].size+1;
  return id;
}

il void rebuild(RG int id){  //重构
  sum=0; recycle(id);
  RG int fa=t[id].fa,Son=( t[ t[id].fa ].son[1]==id );
  RG int cur=build(1,sum);
  t[ t[fa].son[Son]=cur ].fa=fa;
  if(id==root) root=cur;
}

il void insert(RG int x){
  RG int now=root,cur=++cnt;
  t[cur].size=1,t[cur].num=x;
  while(1){  //插入维护序列,左小右大
    t[now].size++;
    RG bool Son=(x>=t[now].num);
    if( t[now].son[Son] ) now=t[now].son[Son];
    else{
      t[ t[now].son[Son]=cur ].fa=now;
      break;
    }
  }
  RG int flag=0;
  for(RG int i=cur;i;i=t[i].fa) if(!balance(i)) flag=i;
  if(flag) rebuild(flag); //插入往往会导致不平衡,这时只需要重建不平衡的子树即可
}

il int get_num(RG int x){  //查询 x 在树中的节点编号
  RG int now=root;
  while(1){
    if(t[now].num==x) return now;
    else now=t[now].son[ t[now].num<x ];
  }
}

il void erase(RG int id){  //删除
  if(t[id].son[0] && t[id].son[1]){
    RG int cur=t[id].son[0];
    while(t[cur].son[1]) cur=t[cur].son[1];
    t[id].num=t[cur].num; id=cur;
  }  //删除操作需要找到左子树的最后一个节点或右子树的第一个节点来顶替,优先找左子树
  RG int Son=(t[id].son[0]) ? t[id].son[0]:t[id].son[1];
  RG int k=( t[ t[id].fa ].son[1]==id );
  t[ t[ t[id].fa ].son[k]=Son ].fa=t[id].fa;
  for(RG int i=t[id].fa;i;i=t[i].fa) t[i].size--;
  if(id==root) root=Son;
}

il int get_rank(RG int x){  //查 x 的排名
  RG int now=root,ans=0;
  while(now){
    if(t[now].num<x) ans+=t[ t[now].son[0] ].size+1,now=t[now].son[1];
    else now=t[now].son[0];
  }
  return ans;
}

il int get_kth(RG int x){  //查树中的第 k 个数
  RG int now=root;
  while(1){
    if(t[ t[now].son[0] ].size==x-1) return now;
    else if(t[ t[now].son[0] ].size>=x) now=t[now].son[0];
    else x-=t[ t[now].son[0] ].size+1,now=t[now].son[1];
  }
  return now;
}

il int get_front(RG int x){  //找前驱,即左子树最后一个点
  int now=root,ans=-inf;
  while(now){
    if(t[now].num<x) ans=max(ans,t[now].num),now=t[now].son[1];
    else now=t[now].son[0];
  }
  return ans;
}

il int get_behind(RG int x){  //找后继,即右子树第一个点
 h RG int now=root,ans=inf;
  while(now){
    if(t[now].num>x) ans=min(ans,t[now].num),now=t[now].son[0];
    else now=t[now].son[1];
  }
  return ans;
}

il void init(){
  cnt=2,root=1;
  t[1].num=-inf,t[1].size=2,t[1].son[1]=2;
  t[2].num=inf,t[2].size=1,t[2].fa=1;
}

il void work(){
  n=gi(); RG int type,x;
  for(RG int i=1;i<=n;i++){
    type=gi(),x=gi();
    if(type==1) insert(x);
    if(type==2) erase( get_num(x) );
    if(type==3) printf("%d\n",get_rank(x));
    if(type==4) printf("%d\n",t[ get_kth(x+1) ].num);
    if(type==5) printf("%d\n",get_front(x));
    if(type==6) printf("%d\n",get_behind(x));
  }
}

int main(){ init(); work(); return 0; }//贯彻一行 main() 的习惯

  

转载于:https://www.cnblogs.com/Hero-of-someone/p/7260332.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
好的,以下是用 C++ 实现替罪羊的代码。替罪羊是一种平衡二叉,可以支持插入、删除、查找等操作,并且可以在插入或删除节点时自动平衡树的结构。 ```C++ #include <iostream> #include <algorithm> using namespace std; template <typename T> struct TreeNode { T val; int size; TreeNode<T> *left, *right, *parent; TreeNode(T val = 0, TreeNode<T> *parent = nullptr, TreeNode<T> *left = nullptr, TreeNode<T> *right = nullptr, int size = 1) : val(val), parent(parent), left(left), right(right), size(size) {} }; // 计算节点的大小 template <typename T> int getSize(TreeNode<T>* node) { return node ? node->size : 0; } // 更新节点的大小 template <typename T> void updateSize(TreeNode<T>* node) { if (node) { node->size = 1 + getSize(node->left) + getSize(node->right); } } // 向左旋转 template <typename T> void rotateLeft(TreeNode<T>* node) { TreeNode<T>* temp = node->right; if (temp) { node->right = temp->left; if (temp->left) { temp->left->parent = node; } temp->parent = node->parent; if (!node->parent) { node->parent = temp; } else if (node == node->parent->left) { node->parent->left = temp; } else { node->parent->right = temp; } temp->left = node; node->parent = temp; updateSize(node); updateSize(temp); } } // 向右旋转 template <typename T> void rotateRight(TreeNode<T>* node) { TreeNode<T>* temp = node->left; if (temp) { node->left = temp->right; if (temp->right) { temp->right->parent = node; } temp->parent = node->parent; if (!node->parent) { node->parent = temp; } else if (node == node->parent->right) { node->parent->right = temp; } else { node->parent->left = temp; } temp->right = node; node->parent = temp; updateSize(node); updateSize(temp); } } // 插入节点 template <typename T> TreeNode<T>* insert(TreeNode<T>* root, T val) { if (!root) { return new TreeNode<T>(val); } if (val < root->val) { root->left = insert(root->left, val); root->left->parent = root; } else { root->right = insert(root->right, val); root->right->parent = root; } updateSize(root); int lSize = getSize(root->left); int rSize = getSize(root->right); if (lSize > 2 * rSize || rSize > 2 * lSize) { int size = lSize + rSize + 1; TreeNode<T>* node = root; TreeNode<T>* parent = nullptr; while (node) { if (2 * getSize(node->left) < size && 2 * getSize(node->right) < size) { break; } parent = node; if (getSize(node->left) > getSize(node->right)) { node = node->left; } else { node = node->right; } } if (!parent) { root = node; } else if (node == parent->left) { parent->left = node; } else { parent->right = node; } node->parent = parent; while (node != root) { node = node->parent; updateSize(node); } } return root; } // 删除节点 template <typename T> TreeNode<T>* remove(TreeNode<T>* root, T val) { TreeNode<T>* node = root; while (node && node->val != val) { if (val < node->val) { node = node->left; } else { node = node->right; } } if (!node) { return root; } if (node->left && node->right) { TreeNode<T>* temp = node->right; while (temp->left) { temp = temp->left; } node->val = temp->val; node = temp; } TreeNode<T>* parent = node->parent; TreeNode<T>* child = node->left ? node->left : node->right; if (child) { child->parent = parent; } if (!parent) { root = child; } else if (node == parent->left) { parent->left = child; } else { parent->right = child; } while (parent) { updateSize(parent); int lSize = getSize(parent->left); int rSize = getSize(parent->right); if (lSize > 2 * rSize || rSize > 2 * lSize) { int size = lSize + rSize + 1; node = parent; parent = node->parent; if (!parent) { root = node; } else if (node == parent->left) { parent->left = node; } else { parent->right = node; } node->parent = parent; while (node != root) { node = node->parent; updateSize(node); } break; } node = parent; parent = node->parent; } delete node; return root; } // 查找节点 template <typename T> TreeNode<T>* find(TreeNode<T>* root, T val) { TreeNode<T>* node = root; while (node && node->val != val) { if (val < node->val) { node = node->left; } else { node = node->right; } } return node; } // 中序遍历 template <typename T> void inorder(TreeNode<T>* root) { if (root) { inorder(root->left); cout << root->val << " "; inorder(root->right); } } int main() { TreeNode<int>* root = nullptr; root = insert(root, 5); root = insert(root, 3); root = insert(root, 7); root = insert(root, 2); root = insert(root, 4); root = insert(root, 6); root = insert(root, 8); cout << "Inorder traversal of the constructed tree: "; inorder(root); cout << endl; root = remove(root, 3); root = remove(root, 6); cout << "Inorder traversal after deletion of 3 and 6: "; inorder(root); cout << endl; return 0; } ``` 在上面的代码中,我们使用了模板来支持不同类型的节点值。我们首先定义了一个 TreeNode 结构体,其中包含节点的值、大小、左子、右子和父节点等信息。然后我们实现了一些辅助函数,如 getSize、updateSize、rotateLeft 和 rotateRight 等函数,这些函数可以帮助我们更新节点的信息并且实现了左旋和右旋操作。 接下来,我们实现了 insert、remove 和 find 等操作。insert 操作用于插入节点,如果的不平衡程度超过了阈值,我们就需要进行重构操作。remove 操作用于删除节点,如果删除节点后的不平衡程度超过了阈值,我们也需要进行重构操作。find 操作用于查找节点,返回节点的指针。 最后,我们实现了一个简单的测试用例,插入一些节点并删除一些节点,最后输出中序遍历的结果。 希望这份代码可以帮助你更好地理解和实现替罪羊
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值