算法的理論學習可右轉Creeper_LKF大佬的洛谷日報
一個優化算法理論時間復雜度的實例點這里
另一個實例點這里
時間復雜度\(O(n)\),算常數的話要乘位長。
蒟蒻參考了Creeper_LKF大佬的模板,並在通用性上面稍微提升了一點。可以兼容所有存儲整數的基本類型,以及在此基礎上構建的結構體類型(多關鍵字排序時,優先級高的關鍵字默認需要在結構體中靠后)。
函數原型
template<typename T>
void Radixsort(T*fst,T*lst,T*buf,int*op)
T
即為待排序的類型名,fst
lst
為首尾指針(和sort
一樣),buf
為緩沖區指針,op
為操作列表。
\(op[i]\)提供類型的第\(i\)個字節的比較方式,具體來說有\(5\)種取值。
\(-1\):該字節不是排序的關鍵字。
\(0\):以該字節為基准從小到大排序。
\(1\):以該字節為基准從大到小排序。
\(2\):以該字節為基准從小到大排序,且該字節的最高位是有符號整形的符號位。
\(3\):以該字節為基准從大到小排序,且該字節的最高位是有符號整形的符號位。
例如,對int
從小到大排序,則應將\(\{0,0,0,2\}\)傳入\(op\)。
對結構體unsigned int,int
以前一個為關鍵字從大到小排序,則代碼大致寫成
Radixsort(a,a+n,buf,new int[8]{1,1,1,1,-1,-1,-1,-1});
對長度為\(n\)的int
數組排序效率對比如下:(STL不吸氧是真的布星)
然而,Radixsort
的運行時間與待排序類型的關鍵字位長總和成正比(upd:蒟蒻目測和總位長也有關,猜測是因為訪問步長增加導致緩存刷新次數增加。例如,對long long
排序大約是對int
排序的三倍時間)。
而std::sort
受此的影響小多了。當總位長在\(10\)位以上時,開O2以后兩者的差距很小了。所以綜合實現難度方面,int
多關鍵字和long long
等用開O2的std::sort
就夠了。
至於實數類型,Radixsort
不能直接資磁。double
是\(8\)位的用std::sort
就好了。至於如果是在想從小到大排float
的話,必須膜改一下數組,將所有的負實數強行除了符號位都按位取反以后,傳入\(\{0,0,0,2\}\),最后還要還原回來,實在是太麻煩了。
#include<bits/stdc++.h>
#define UC unsigned char
using namespace std;
template<typename T>
void Radixsort(T*fst,T*lst,T*buf,int*op){
static int b[0x100];
int Len=lst-fst,Sz=sizeof(T),at=0,i,j;
UC*bgn,*end,tmp;
for(i=0;i<Sz;++i){
if(op[i]==-1)continue;
bgn=(UC*)fst+i;end=(UC*)lst+i;
tmp=((op[i]&1)?0xff:0)^((op[i]&2)?0x80:0);
memset(b,0,sizeof(b));
for(UC*it=bgn;it!=end;it+=Sz)++b[tmp^*it];
for(j=1;j<0x100;++j)b[j]+=b[j-1];
for(UC*it=end;it!=bgn;buf[--b[tmp^*(it-=Sz)]]=*--lst);
lst=buf+Len;swap(fst,buf);at^=1;
}
if(at)memcpy(buf,fst,Sz*Len);
}
有沒有覺得很好實現呢?比什么后綴排序不知道好寫到哪里去了
這樣實現很簡短,但常數沒有卡到極限,b桶數組的初始化部分可以強行展開出來並行計算。