关于桶排序的算法思想就不再叙述了,目前桶排序主要用于在O(n)的时间内处理正数的排序问题, 然而一旦遇到负数问题,就只能把数分成两堆,对正负数分开去排序,这样无疑比较麻烦。今天我做了一个桶排序的题,对于输入都是32位整形数列进行桶排序,值想到了一个可以同时处理正负数的算法。
通常我们的输入都是十进制数,桶排序都是设置为10个桶,从后向前每次对每一个位进行排序,这样虽然是O(n),但是除法操作会占用大量的时间。我的改进思路是将桶设置为16个,即2^4个桶,32位的int型整数看做16进制是8位,最高位是符号位,这样可以用移位操作代替除法操作,大大提高了效率。桶排序的空间复杂度是O(n) ,即需要开辟新数组存储所有的数据,桶的数量不影响空间复杂度。代码如下:
void bucket_sort(vector<int>& nums){
vector<vector<int>> bucket(16, vector<int>());
//第一轮,将数组按照最低四位2进制数存入所有的桶中
for (auto i : nums){
bucket[i & 15].push_back(i);
}
//循环七轮,进行桶排序
for (int i = 1; i < 8; i++){
vector<vector<int>> bucket2(16, vector<int>());
//计算移位和掩码
int shift = i * 4;
int mask = 0xf<< shift;
for (auto v : bucket)
//注意 C++ unsigned的右移是逻辑右移,前面不补0
for (unsigned num : v)
bucket2[(num&mask) >> shift].push_back(num);
//每轮之后交换原桶和临时桶
swap(bucket, bucket2);
}
//排序之后,交换正负数的位置
int pos = 0;
for (int i = 0; i < 8; i++) swap(bucket[i], bucket[i + 8]);
for (auto v : bucket)
for (auto i : v)
nums[pos++] = i;
}
唯一需要说明的就是,排序之后,由于负数的符号位是1,正数的符号位是0,所以0-7个桶存放的是正数,8-15个桶存放的是负数;除此之外,正数是从小到大排列的,负数由于是补码存放,其绝对值为取反之后再加1,所以负数是按照绝对值从大到小排列的,即负数也是从小到大排列的。所以只需要把0-7桶放到8-15桶的后面,即可完成排序。