• 块状链表 学习笔记


    感谢大佬 @w33z8kqrqk8zzzx33

    用块状链表实现平衡树,是怎么回事呢?快来跟小编看看吧~

    前置知识

    vector 的部分用法

    vector 就是变长数组。由于 STL 制作者们八仙过海,所以它的一些操作常数极小(相比于普通数组)。

    首先注意,vec 的第一个位置下标是 0;而且大多函数都是左闭右开的(就像 sort 一样,你要写 sort(a+1,a+n+1) 而不是 sort(a+1,a+n),就是因为 sort(l,r) 的范围是 ([l,r))([l,r-1]))。

    • vec.begin() 它的第一个位置(如果你想知道 vec 第一个位置的数字是什么,你可以用 vec.front()vec[0])。

    • vec.end() 它的最后一个位置还要后一位(所以实际的最后一位是 --vec.end())(与 vec.front() 同理,最后一个位置的数字vec.back())。

    • vec[wei]wei 个位置的数字

    • vec.size() 它有多少个元素。

    • vec.emplace_back(x) 在它的最后放一个新数字 x(和 push_back 是一个东西),(O(1))

    • vec.emplace(wei,x) 在它的 wei 这个位置前面插入一个新位置,新位置的数字是 x(和 insert 是一个东西),极小常数的 (O(n))

    • vec.erase(wei) 删除 wei 这个位置,极小常数的 (O(n))

    • lower_bound(vec.begin(),vec.end(),x) 查询整个 vec 第一个大于等于 x 的数字的位置是什么。这个函数也是左闭右开的,但是因为 vec.end() 刚好指向最后一位的下一位,所以可以视作整个 vec(O(log n))

    • upper_bound(vec.begin(),vec.end(),x) 查询整个 vec 第一个大于 x 的数字的位置是什么。和上面同理。

    平衡树题意

    维护一个多重集合,需支持下列操作:

    1. 插入或删除一个数 (x)
    2. 查询 (x) 排名;
    3. 查询排名为 (x) 的数字是什么;
    4. 查询 (x) 的前驱/后继。

    块状链表

    我们想到一个暴力做法:维护一个 vec,使得里面的数字都是从小到大排好序的。

    对于四个操作:

    1. 二分 (x) 应该在的位置,暴力插入/删除。
    2. 二分 (x) 应该在的位置。
    3. cout<<vec[x]
    4. 二分 (x) 应该在的位置。

    瓶颈显然在于操作 1 ,是小常数 (O(n))

    那么考虑分块。为了方便,设 (B=sqrt n),分成了 B 个大小为 B 的块。

    然后二分 (x) 的位置,就可以转为另一个方法:

    依次遍历每个块,看一下块尾的元素是否小于 (x)。如果,则 (x) 的位置就在这个块中,在块内二分即可。这是 (O(B)) 的哟~

    暴力插入/删除的话,也变成了小常数 (O(B))(实测跑的飞快,就像发疯了的 (O(log n)) 一样!)。

    但是如果我们不停地在同一个块进行插入,这个块变得很大,会退化成 (O(n))

    解决方案是,如果有一个块的块长大于 (B) 了,那么就把它们掰成两个 (frac{B}{2}) 的块

    这样保证时间复杂度了,但是还有一些细节:

    1. 为了仍能保证块的大小顺序(即把所有块合成一起时,仍是从小到大排序的),我们需要用链表把所有的块串起来,这样掰开块的时候就是 (O(1)) 的了。
    2. 因为链表不支持定点访问,操作 3 的方法要改变了。设立当前排名 (cnt),我们一次遍历每一个块,就让 (cnt) 增加块的大小。如果 (cntge x) 了,代表就在当前块中,块内二分即可。
    3. 一开始把数列加进去的时候,是 (frac{B}{2}) 个为一组,放进块中。

    代码

    题目 【模板】普通平衡树

    链表可以直接用 vector 代替,也就是 vecvec!!1

    代码非常短,于是块状链表也被称作 “五分钟平衡树”。

    #include<bits/stdc++.h>
    #define rep(i,x,y) for(int i=x;i<=y;++i)
    #define ltor(id) kzlb[id].begin(),kzlb[id].end() 
    #define iter vector <int> ::iterator 
    #define pb emplace_back
    using namespace std;
    const int n7=101234,inf=1e7+1;
    int T,B;
    vector < vector <int> > kzlb;
    
    int rd(){
    	int shu=0;bool fu=0;char ch=getchar();
    	while( !isdigit(ch) ){if(ch=='-')fu=1;ch=getchar();}
    	while( isdigit(ch) )shu=(shu<<1)+(shu<<3)+ch-'0',ch=getchar();
    	return fu?-shu:shu;
    }
    
    void wt(int z){
    	if(z<0)putchar('-'),z=-z;
    	if(z>9)wt(z/10);
    	putchar(z%10+'0');
    }
    
    int ffind(int x){
    	rep(i,0,kzlb.size()-1){
    		if(kzlb[i].back()>=x)return i;
    	}
    }
    
    void isert(int x){
    	int id=ffind(x);
    	kzlb[id].emplace(lower_bound(ltor(id),x),x);
    	if(kzlb[id].size()>B){
    		kzlb.emplace(kzlb.begin()+id+1,kzlb[id].begin()+B/2, kzlb[id].end() );
    		kzlb[id].erase(kzlb[id].begin()+B/2, kzlb[id].end() );
    	}
    }
    
    void erase(int x){
    	int id=ffind(x);
    	kzlb[id].erase( lower_bound(ltor(id),x) );
    	if( !kzlb[id].size() )kzlb.erase(kzlb.begin()+id);
    }
    
    int Qkth(int x){
    	rep(i,0,kzlb.size()-1){
    		if(kzlb[i].size()>=x)return kzlb[i][x-1];
    		else x-=kzlb[i].size();
    	}
    }
    
    int Qnum(int x){
    	int cnt=0;
    	rep(i,0,kzlb.size()-1){
    		if(kzlb[i].back()>=x){
    			cnt+=lower_bound(ltor(i),x)-kzlb[i].begin();
    			return cnt+1;
    		}
    		else cnt+=kzlb[i].size();
    	}
    }
    
    int Qpre(int x){
    	int id=ffind(x);
    	iter wei=lower_bound(ltor(id),x);
    	if( wei==kzlb[id].begin() )return kzlb[id-1].back();
    	else return *--wei;
    }
    
    int Qnxt(int x){
    	int id=ffind(x+1);
    	iter wei=upper_bound(ltor(id),x);
    	//左闭右开,所以wei=end表示找不到 
    	if( wei==kzlb[id].end() )return kzlb[id+1].front();
    	else return *wei;
    }
    
    int main(){
    	kzlb.pb();
    	kzlb.back().pb(inf);
    	T=rd(),B=sqrt(T);
    	while(T--){
    		int sys=rd(),x=rd();
    		if(sys==1)isert(x);
    		if(sys==2)erase(x);
    		if(sys==3)wt( Qnum(x) ),putchar('
    ');
    		if(sys==4)wt( Qkth(x) ),putchar('
    ');
    		if(sys==5)wt( Qpre(x) ),putchar('
    ');
    		if(sys==6)wt( Qnxt(x) ),putchar('
    ');
    	} 
    	return 0;
    }
    
    
  • 相关阅读:
    使用Newtonsoft进行JSON序列化时将枚举序列化为字符串的方法
    通过javac导出Jar包
    JAVA中JDBC连接Mysql数据库简单测试
    使用Swashbuckle.AspNetCore生成.NetCore WEBAPI的接口文档
    CentOS7中使用yum安装Nginx的方法
    Dockerfile 指令 ADD 和 COPY介绍
    Azure Web应用中设置静态虚拟目录的方法(比如部署Django需要用到)
    pip常用命令(转载)
    android发布新版忘记keystore(jks)密码终极解决方案
    IntelliJ Idea 常用快捷键列表
  • 原文地址:https://www.cnblogs.com/BlankAo/p/14724158.html
Copyright © 2020-2023  润新知