计数排序与基数排序均为桶排序的特殊方法
这两天的夜还真是冷啊...
那么,计数排序是什么样的排序呢?
计数排序不是比较排序算法,该算法于1954年由 Harold H. Seward提出,通过计数将时间复杂度降到了O(N)
。
计数排序算法的步骤又是怎样的呢?
- 第一步:找出原数组中元素值最大的,记为
max
。 - 第二步:创建一个新数组
count
,其长度是max
加1,其元素默认值都为0。 - 第三步:遍历原数组中的元素,以原数组中的元素作为
count
数组的索引,以原数组中的元素出现次数作为count
数组的元素值。 - 第四步:创建结果数组
result
,起始索引index
。 - 第五步:遍历
count
数组,找出其中元素值大于0的元素,将其对应的索引作为元素值填充到result
数组中去,每处理一次,count
中的该元素值减1,直到该元素值不大于0,依次处理count
中剩下的元素。 - 第六步:返回结果数组
result
。
这样很抽象,有没有让人很容易理解的讲法,比如说图解呢?
但是这样浪费空间了呀!
比如一组数据{101,109,108,102,110,107,103}
,其中最大值为110,按照之前的思路,我们需要创建一个长度为111的计数数组,但是我们可以发现,它前面的[0,100]
的空间完全浪费了。
那怎样优化呢?
将数组长度定为max-min+1
,即不仅要找出最大值,还要找出最小值,根据两者的差来确定计数数组的长度。
先放个段子轻松下
下面我们就用优化后的思路来解决一道面试中常考的手撕排序题
题面
提交情况
计数排序代码(优选)
class Solution {
public:
vector<int> sortArray(vector<int>& nums) {
int n = nums.size();
int max = nums[0], min = nums[0];
for(int i=1; i<n; i++) {
if(nums[i] > max) max = nums[i];
if(nums[i] < min) min = nums[i];
}
vector<int> count(max-min+1,0);
for(int i=0; i<n; i++)
count[nums[i]-min]++;
int k=0;
for(int i=0; i<max-min+1; i++) {
while(count[i]--) {
nums[k++] = i+min;
}
}
return nums;
}
};
对应的基数排序代码
class Solution {
public:
vector<int> sortArray(vector<int>& nums) {
int n = nums.size();
int max = abs(nums[0]);
for(int i=1; i<n; i++)
if(max<abs(nums[i]))
max = abs(nums[i]);
int w = 0;
while(max>0) {
max /= 10;
w++;
}
int flag = 1;
vector<int> ans(n);
for(int i=0; i<w; i++) {
vector<int> bucket(19,0);
for(int j=0; j<n; j++) {
int temp = nums[j]/flag%10+9;
bucket[temp]++;
}
for(int j=1; j<19; j++)
bucket[j] += bucket[j-1];
for(int j=n-1; j>=0; j--) {
int temp = nums[j]/flag%10+9;
ans[--bucket[temp]] = nums[j];
}
nums.swap(ans);
flag*=10;
}
return nums;
}
};