zoukankan      html  css  js  c++  java
  • 左偏树基础教学

    感觉这两天数据结构没啥可说的

    但是有个左偏树 听起来很高级但是特别简单

    那我就说一下吧

     

    左偏树教学


    左偏树 顾名思义 往左偏的树

    它肯定不是BST 因为我们的终极目标是把他转成一条链什么的

    左偏树并不是为了快速访问所有的节点而设计的,它的目的是快速访问最小节点 以及在对树修改后快速的恢复堆性质。

    恰好走了相反的路的左偏树 依旧靠着自己的特点完成了自己的职能

    事实上往右偏是一样的因为二叉树可以转

    你只要知道以下几点:

    (以下均以小根堆为例)

    [性质 1] 节点的键值小于或等于它的左右子节点的键值。

    堆性质。便于我们更高效的查找最值

    [性质 2] 节点的左子节点的距离不小于右子节点的距离。

    这里我们定义一个距离数组dist[]

    它表示的就是节点到叶子的距离

    同时 如果一个节点只有左儿子(不可能只有右儿子)它的dist值是0

    所以举个例子 一条链每个节点dist都是0

     

    好 研究完两个性质 我们考虑怎么实现操作

    1.插入 查询

    同二叉堆

    2.合并两颗左偏树

    重头戏来了!

    让我们先看代码

    1 int Merge(int x,int y) {
    2     if(!x || !y) return x | y;
    3     if(v[x] > v[y] || (v[x] == v[y] && x > y)) swap(x,y);
    4     rs[x] = Merge(rs[x],y);
    5     pa[rs[x]] = x;
    6     if(dist[ls[x]] < dist[rs[x]]) swap(ls[x],rs[x]);
    7     dist[x] = dist[rs[x]] + 1;
    8     return x;
    9 }

    是不是特别短?

    (啪!)

    我们考虑暴力合并启发式合并  拆除较小的堆中的每一个节点加进较大的节点中 复杂度O(nlgn*单步复杂度) = O(nlg2n)或者O(nlgn)什么的

    但是 优秀的左偏树可以做到O(n)完成n个节点的合并 O(lgn)完成两颗n个节点子树的合并

    可能会有dalao觉得直接讲第二个就能显然第一个 但是我还是suo一下

    其实我们的复杂度瓶颈就在于合并 因为查询直接O(1) 二叉堆都能做到

    所以左偏树可以理解为 事先留出右边给要合并的另一个树

    树是递归定义的 所以每次合并的时候只需要递归处理就OK

    所以合并的时候:

    1.找到两颗树中树根值较小的一棵树做新树根

    2.把右子树和另一棵树合并直到一棵树为空为止

    关于子问题的信息 我们只需要子树树根的编号和dist

    然后考虑可能放下去合并的树比较大 导致右边比左边多

    判断一下直接交换就可以了

    然后左偏树左儿子dist一定大于右儿子 所以整棵树的dist就是右儿子dist+1

    如果没有右儿子dist是0所以一开始规定dist[0] = -1

    这里就可以发现一个事情 由于左儿子较大 而且求根节点dist的时候我们不去管他

    所以 若一棵左偏树的dist为 k,则这棵左偏树至少有2k+1-1个节点。

    所以 复杂度得到了保证,即:

    一棵 N 个节点的左偏树距离最多为[log2(n+1)-1](下取整)

    由于分解的次数不会超过 log2(n)+log2(m) 其中 n 和 m 分别为左偏树 A 和 B 的节点个数。因此合并操作最坏情况下的时间复杂度为O(lg(nm)) 也就是O(lgn)

    同时 插入操作也可以看成是一个只有一个点的左偏树和一颗大树合并 所以是O(lgn)

    3.删除某个特定元素

    这个做不到

    但是我们能做到删除堆顶最值 而大部分左偏树的题目只有这个要求

    删完之后把左右子树合并就OK了

    --------------------更新分割线----------------

    然后这里填一下坑 如何O(n)建一棵左偏树

    其实特别简单 考虑合并果子 每次贪心找最小的合并

    那么需要:

    合并n/2次1个节点

    合并n/4次2个节点

    合并n/8次4个节点

    ...

    合并复杂度是严格O(log2n)

    所以总复杂度就是O(n*Σi/2^i) = O(n*(2 - (k+2)/2^k)) = O(n)

    (常数忽略,k是总深度)

    -----------------------------------------------------

    Code:(luoguP3377)

     1 #include<cstdio>
     2 #include<cstring>
     3 #include<algorithm>
     4 #include<cmath>
     5 #include<queue>
     6 #include<vector>
     7 #define ms(a,b) memset(a,b,sizeof a)
     8 #define rep(i,a,n) for(int i = a;i <= n;i++)
     9 #define per(i,n,a) for(int i = n;i >= a;i--)
    10 #define inf 2147483647
    11 using namespace std;
    12 typedef long long ll;
    13 ll read() {
    14     ll as = 0,fu = 1;
    15     char c = getchar();
    16     while(c < '0' || c > '9') {
    17         if(c == '-') fu = -1;
    18         c = getchar();
    19     }
    20     while(c >= '0' && c <= '9') {
    21         as = as * 10 + c - '0';
    22         c = getchar();
    23     }
    24     return as * fu;
    25 }
    26 const int N = 500005;
    27 //head
    28 int n,m;
    29 int v[N],pa[N];
    30 int ls[N],rs[N],dist[N];
    31 int Merge(int x,int y) {
    32     if(!x || !y) return x | y;
    33     if(v[x] > v[y] || v[x] == v[y] && x > y) swap(x,y);
    34     rs[x] = Merge(rs[x],y);
    35     pa[rs[x]] = x;
    36     if(dist[ls[x]] < dist[rs[x]]) swap(ls[x],rs[x]);
    37     dist[x] = dist[rs[x]] + 1;
    38     return x;
    39 }
    40 
    41 int Del(int x) {
    42     int tmp = v[x];
    43     v[x] = -1;
    44     pa[ls[x]] = pa[rs[x]] = 0;
    45     Merge(ls[x],rs[x]);
    46     return tmp;
    47 }
    48 
    49 int gpa(int x) {
    50     while(pa[x]) x = pa[x];
    51     return x;
    52 }
    53 
    54 int main() {
    55     n = read(),m = read();
    56     rep(i,1,n) v[i] = read();
    57     while(m--) {
    58     int op = read();
    59     if(op == 1) {
    60         int x = read();
    61         int y = read();
    62         if(~v[x] && ~v[y] && x ^ y) Merge(gpa(x),gpa(y));
    63     } else {
    64         int x = read();
    65         if(v[x] == -1) {puts("-1");continue;}
    66         int y = gpa(x);
    67         printf("%d
    ",Del(y));
    68     }
    69     }
    70     return 0;
    71 }
    View Code

     

    > 别忘了 总有人在等着你
  • 相关阅读:
    python-函数-实现学生管理系统,完成对学员的增,删,改,查和退出学生管理系统。
    python-函数-定义一个函数,实现两个数四则运算,要注意有3个参数,分别是运算符和两个运算的数字.
    Python-函数-continue、break、pass
    python-函数-写函数,接收n个数字,求这些数字的和
    python-函数-写函数,检查传入的字符串是否含有空字符串,返回结果,包含空字符串返回True,不包含返回False
    python-函数-写函数,判断用户传入的对象(列表)长度是否大于5,如果大于5,那么仅保留前五个长度的内容并返回。不大于5返回本身
    Python-函数-写函数,获取传入列表的所有奇数位索引对应的元素,并将其作为新列表返回
    PHP解析json字符串问题
    《算法技术手册》-Algorithms in a nutshell 作者: George T. Heineman
    PHP面向对象设计总结
  • 原文地址:https://www.cnblogs.com/yuyanjiaB/p/9804869.html
Copyright © 2011-2022 走看看