C++基数排序另一种实现

Intro

这个版本的基数排序和算法导论的不太相同,思想都是一样的,但是感觉比那个好理解很多
选用unsigned int 也就是u32 作为例子,按位来,一共只有两个桶,所以要比32次。但其实基数排序非常有意思。
在于这些都是可变的

设待排序列为n个记录,d个关键码,关键码的取值范围为radix,排序时间复杂度为O(d(n+radix)),
空间复杂度2n
这里n ∈[1e6,1e9) 做了下测试,d=32(因为32bit) ,radix = 2

思路一共两条,最高位优先MSD和最低位优先LSD,都是按位分桶嘛,也就是一个从头往尾排,一个从尾往头排。两个都可以,但是MSD会有点麻烦,比完高位要是一样还得再比一下低位,要分出很多子序列。而且并没有感受到有什么特别的优点,所以就LSD了

算法流程

先设一个tmp数组,也就是桶。但是因为按位来,0和1可以是一个大桶里的两个小桶
从最低位开始,0放一桶,1放一桶,0从tmp的第0位开始装,1从最后一位装。
装完合并回data,但是注意1那个桶的顺序要弄正常一点,之前是倒着装的。
合并的时候就是0放前1放后,这样最后是升序的

讨论

编程

这个之前学排序都是用泛型写的,基数排序不是很容易泛型。如果不是unsigned , 就要对符号位单独处理。如果是浮点类型,也可以用基数排序!!!之前被人怼了,当时没学IEEE浮点标准。肯定要对尾数和阶码作特殊处理,我懒得搞了网上有论文。

但是这说明一个道理!如果想兼容之前的排序代码,就要用模板特化

template<typename T> 
void radixSort(T* data,int left,int right){}

template<> void radixSort<u32>(u32* data,int left, int right)
{
    //implement
}

也就是说,对不同的typename都应该分别实现不同的方法,调用的时候会调用对应的函数。

时间复杂度与空间复杂度

O(d(n+radix))这个在intro里面提了一下,可以稍微简化一下看一下效果,忽略太小的:
O(d*n) 看着是线性的,d一般不会太大,这里的例子是32,但是快排nlgn的lgn不一定就比d大。这样分析的话,数组长度要大于1<<32之后才会看到效果。

但是,基数排序是一个比较容易并行的排序,并行之后还可以大概除一个常数(代码要写的非常好才可以把大概去掉)这就可能会比快排好啦。

实验

就是vanilla quick sort 和radix sort比。

#数组长度1e7
quick sort 1.04497
radix sort 1.65082

#数组长度1e8
quick sort 11.9672
radix sort 16.6019



#数组长度1e9
quick sort 133.076
# 爆内存啦

为什么会爆内存呢?u32是4byte,1e9个u32也就是4GB!空间复杂度2N刚好我内存是8GB。然后就只能重启了= =
这样的话是不如快排快的,改进思路就是换基数和CUDA并行

主要代码

template<typename T> 
void radixSort(T* data,int left,int right){}

template<> void radixSort<u32>(u32* data,int left, int right)
{
    const int len = right - left + 1;
    u32* tmp = new u32[len];
    for(u32 bit = 0;bit < 32;bit++)
    {

        u32 cnt0 = 0;
        u32 cnt1 = 0;

        for(u32 i = 0;i < len;i++)
        {
            const u32 d = data[i];
            const u32 bit_mask = (1<<bit);

            if(d & bit_mask)
                tmp[cnt1++] = d;
            else
                tmp[len - cnt0++ - 1] = d;
        }

        for(u32 i = 0;i < cnt0;i++)
            data[i] = tmp[len - i -1];
        for(u32 i = 0;cnt0 < len;i++)
            data[cnt0++] = tmp[i];
    }
    delete[] tmp;
}

全部代码

#include<thread>
#include<atomic>
#include<ctime>
#include<iostream>
#include<ctime>
#include<cstdlib>
#include<algorithm>
using namespace std;


using u32 = unsigned int;
using TYPE = u32;
const int LEN =(int)1e6;


template<typename T=int>
void disp(T* data,int l)
{
//  cout<<__func__<<": ";
    for(int i = 0;i<l;i++)
    {
        if((i!=0) && (i%10==0))
            cout<<endl;
        cout<<data[i]<<" ";
    }
    cout<<endl;
}
template<typename T=int>
void init(T* data,int len)
{
    srand(unsigned(time(0)));
    for(int i = 0;i<len;i++)
        data[i] = (T)rand();
}
template<typename T=int>
void bubbleSort(T* data,int left = 0,int right = LEN-1)
{
    for(int i = left; i <= right; i++)  
        for (int j = i + 1; j <= right; j++)  
            if (data[i] > data[j])  
                swap(data[i], data[j]);  
}
template<typename T=int>
void qsort(T* data,int left = 0,int right = LEN-1)
{
    if(left < right)
    {
        int l = left;
        int h = right;
        T pivot = data[left];
        while(l<h)
        {
            while(data[h] >= pivot && l < h) h--;
            data[l] = data[h];
            while(data[l]<pivot && l < h) l++;
            data[h] = data[l];
        }
        data[l] = pivot;
        qsort(data,left,l-1);
        qsort(data,l+1,right);
    }
}

bool Sorted = false;

template<typename T=int>
void sortswap(T* data,int index,int right)
{

    if(index == right)
        return;
    if(data[index]>data[index+1])
    {
        swap(data[index],data[index+1]);
        Sorted = false;
    }
}
template<typename T=int>
void oddEvenSort(T* data,int left = 0,int right = LEN-1)
{

    int latch = (right-left)/2 + (right-left)%2;
    thread threads[latch];
    int start = left;

    while(!Sorted)
    {

        Sorted = true;

        for(int i = start,j = 0;j<latch;i+=2,j++)
            threads[j] = thread(sortswap<T>,data,i,right);

        for(int i = 0;i<latch;i++)
            threads[i].join();

        if(start == left)
            start = left +1;
        else
            start = left;
    }
}

template<typename T> 
void radixSort(T* data,int left,int right){}

template<> void radixSort<u32>(u32* data,int left, int right)
{
    const int len = right - left + 1;
    u32* tmp = new u32[len];
    for(u32 bit = 0;bit < 32;bit++)
    {

        u32 cnt0 = 0;
        u32 cnt1 = 0;

        for(u32 i = 0;i < len;i++)
        {
            const u32 d = data[i];
            const u32 bit_mask = (1<<bit);

            if(d & bit_mask)
                tmp[cnt1++] = d;
            else
                tmp[len - cnt0++ - 1] = d;
        }

        for(u32 i = 0;i < cnt0;i++)
            data[i] = tmp[len - i -1];
        for(u32 i = 0;cnt0 < len;i++)
            data[cnt0++] = tmp[i];
    }
    delete[] tmp;
}


template<typename T>
void (*f[])(T*,int,int) = 
{
    qsort,
    //bubbleSort,
    //oddEvenSort,
    radixSort
};
int main()
{

    TYPE* data = new TYPE[LEN];
    for(int i = 0;i<2;i++)
    {
        init<TYPE>(data,LEN);
        auto t1 = clock();
        f<TYPE>[i](data,0,LEN-1);
        cout<<(double)(clock()-t1)/CLOCKS_PER_SEC<<endl;
        //disp(data,100);
    }
    delete[] data;
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值