我们用字典树,从高位到低位进行排序,使用中有类似于基排的思路。
残留问题在于,空间方面,需要我们使用vector或类似的动态扩充的来做。不过,不想去想了,先给个代码:
#include<bits/stdc++.h> using namespace std; const int N=1e5+1,M=256,mod=255; struct node{ int a[M],tot; }t[N<<2]; int cnt,sav[5]; inline void insert(int x){ int now=0; for(int i=1;i<=4;++i){ sav[i]=(x&mod),x>>=8; } int res=0; for(int i=4;i;--i){ int v=sav[i]; res=(res<<8)+v; if(!t[now].a[v]){ t[now].a[v]=++cnt; } now=t[now].a[v]; } ++t[now].tot; } inline void print(int now,int sum){ while(t[now].tot){ --t[now].tot; printf("%d ",sum); } for(int i=0;i<M;++i){ if(t[now].a[i]){ print(t[now].a[i],(sum<<8)+i); } } } int main(){ int n; scanf("%d",&n); for(int i=1;i<=n;++i){ int x; scanf("%d",&x); insert(x); } print(0,0); return 0; }
Update:一个粗糙的想法:我们用vector储存,最终输出排序的时候,每次对每个节点的最多256个子节点排序即可,直接输出排序后的序列的复杂度最多,使用基排会更低(将基数设为4,会有最低复杂度)
此算法的优势:总复杂度在水平,且支持常数级别的增加/删除操作,也可以用来查询kth,前驱,后继,(使用二分的话,每次复杂度最多即为就是,kth维护和,前驱后继分别维护最小值和最大值,然后,前驱后继因为可以直接索引,所以可以不用二分做到)
Update2:发现,上面的方法其实并不优秀,因为如果我们增加树高的话,会使得总负责度更低,比如我们令基数为16,树高变高了,但是空间却可以开下了,就可以避免排序的复杂度了,所以,我们可以根据题目,动态的变化基数大小。
(当然,从总分析来看,基数为8是最优的)
如何做有负数的情况?类似二进制位,我们加一层“符号位”,0为负,1为正即可