平衡树学习笔记

替罪羊树

  • α 平衡因子。 ( 0.5<α<1)

  • 替罪羊树原理:当树不平衡时,中序遍历收集树上的所有节点,然后用二分的方式重构整棵树,使其变成标准的平衡树。查询代价由树高决定,重构代价由重构节点个数和平衡因子决定。

  • 节点信息:

    • val:键值,代表节点储存的信息。
    • count:平衡树最好不要有键值相同的节点, 插入重复的值只需要把对应节点的count 值修改就可以了。
    • left,right:左右孩子指针。
    • diff: 子树中含有的节点数的多少。(即不同的数的个数)
    • size:记录树上总共收集了多少个变量。
  • α 因子越接近 1,重构次数越少,查询复杂度上升,α 越接近 0.5 ,树高越小,重构次数越多,查询时间复杂度下降。


class TZYTree{
public:
#define ls(p)   t[p].l
#define rs(p)   t[p].r
    struct node{
        int l , r;  // 左右孩子指针, 如果没有孩子就为0
        int val;    // 代表当前节点的值
        int diff;   // 代表子树中有多少个节点
        int count;  // 代表当前节点有多少个数
        int size;   // 代表子树中有多少个数
        node() : l(0) , r(0), diff(0),count(0),size(0),val(0)
        {}
    };
    const double alpha = 0.7;   // 平衡因子
    int cnt = 0;                // 代表有序表当前已使用的节点数
    int top = 0;                // 代表最上方不平衡的节点, 如果 top = 0 , 则说明不存在不平衡的节点
    int Head = 0;               // 记录替罪羊树的根节点的下标, 如果表中无元素则下标为 0 
    int father = 0;             // 记录最上方不平衡的节点的父节点, 重构之后可能需要修改指针, 如果是整棵树 , 那么 father = 0
    int side = 0;               // 记录不最上方的不平衡节点是它的父节点的左孩子还是右孩子 , 1为左孩子 , 0 为右孩子
    int pos = 0;                // 记录 order 表中最后一个数据的下标 , 即 1 - pos
    static const int inf = INT_MAX;
    std::vector<node> t;
    std::vector<int> order;
    TZYTree (int n) {
        // n 代表有序表最大数量
        assign(n);
    }
    void assign(int n){
        t.assign( n + 2,node());
        order.assign(n+2,0);

    }
    
    // 新增一个节点
    int add(int x) { 
        t[++cnt].val = x;
        t[cnt].size = t[cnt].diff =t[cnt].count = 1;
        return cnt;
    }


    void up( int p){
        t[p].size = t[ls(p)].size + t[rs(p)].size + t[p].count;
        t[p].diff = t[ls(p)].diff + t[rs(p)].diff + (t[p].count > 0);
    }

    // 收集为中序遍历
    void inorder(int p) {
        if (p !=0)
        {
            inorder(ls(p));
            if(t[p].count >0 ){
                order[++pos] = p; // 注意这里只是中序遍历把节点下标上传
            }
            inorder(rs(p));
        }
    }
    // l 代表order 中的序号 , 中序遍历重构
    int build(int l , int r) {
        if(l > r) return 0;
        int mid = (l + r)/2;
        int head = order[mid];
        ls(head) = build(l,mid-1);
        rs(head) = build(mid+1,r);
        up(head);
        return head;
    }

    void rebuild() {
        if(top != 0) {
            // 即存在不平衡现象
            pos = 0;
            inorder(top);
            if (pos > 0) {
                if( father == 0) {
                    Head = build(1,pos); //  头节点重连
                } else if(side == 1) {
                    ls(father) = build(1,pos);
                } else {
                    rs(father) = build(1,pos);
                }
            }
        }
    }

    bool balance(int p) {
        // 如果子树中的节点数大于 整颗树的节点数乘以 alpha 倍, 则重构
        return alpha * t[p].diff >= std::max(t[ls(p)].diff , t[rs(p)].diff);
    }
    void insert(int number) {
        top = father = side = 0;
        insert(Head,0,0,number);
        rebuild();
    }

    void insert(int p,int f,int s,int num) {
        if(p == 0) {
            if(f == 0) {
                Head = add(num);
            } else if(s == 1) {
                ls(f) = add(num);
            } else {
                rs(f) = add(num);
            }
        } else {
            // assert(p >t.size());
            if( t[p].val == num) {
                t[p].count++;
            } else if(t[p].val > num) {
                insert(ls(p),p,1,num);
            } else {
                insert(rs(p),p,2,num);
            }
            up(p);
            if(!balance(p)) {
                top = p;
                father = f;
                side = s;
            }
        }
    }
    void remove(int p, int f, int s,int num) {
        if(t[p].val == num) {
            t[p].count--;
        } else if(t[p].val > num) {
            remove(ls(p),p,1,num);
        } else {
            remove(rs(p),p,2,num);
        }
        up(p);
        if(!balance(p)) {
            father = f , side = s , top = p;
        }
    }

    void remove(int num) {
        if(rank(num) != rank(num+1)) {
            top = father = side = 0;
            remove(Head,0,0,num);
        }
    }

    int rank(int num) {
        return small(Head,num) + 1;
    }
    int small(int p , int num) {
        if(p == 0) {
            return 0;
        }
        if(t[p].val >= num) {
            return small(ls(p),num);
        } else {
            return t[ls(p)].size + small(rs(p),num) + t[p].count;
        }
    }
    int index(int x) {
        return index(Head,x);
    }
    int index(int p, int x) {
        if(t[ls(p)].size >= x) {
            return index(ls(p),x);
        } else if(t[ls(p)].size + t[p].count >= x) {
            return p;
        } else {
            return index(rs(p),x - t[ls(p)].size - t[p].count);
        }
    }
    // 查找小于某个数的最大值
    int pre(int x) {
        int kth = rank(x);
        if(kth == 1) {
            return -inf;
        } else {
            return index(kth-1);
        }
    }
    // 查找大于某个数的最小值
    int post(int x) {
        int kth = rank(x + 1);
        if(kth == t[Head].size + 1) { // 也就是没有大于某个数的值就放回 inf
            return inf;
        } else {
            return index(kth);
        }
    }
    void clear(){
        Head = cnt = 0;
        t.clear();
        order.clear();
    }

#undef ls   
#undef rs   
};
posted @   -风间琉璃-  阅读(7)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 无需6万激活码!GitHub神秘组织3小时极速复刻Manus,手把手教你使用OpenManus搭建本
· C#/.NET/.NET Core优秀项目和框架2025年2月简报
· Manus爆火,是硬核还是营销?
· 终于写完轮子一部分:tcp代理 了,记录一下
· 【杭电多校比赛记录】2025“钉耙编程”中国大学生算法设计春季联赛(1)
点击右上角即可分享
微信分享提示