一、计数排序
稳定、 当输入的元素是n 个小区间(0到k)内整数时,它的运行时间是 O(n + k),空间复杂度是O(n)。
const int K = 100;
//计数排序:假设输入数据都属于一个小区间内的整数,可用于解决如年龄排序类的问题
//Input:A[0, ..., n-1], 0 <= A[i] < K
//Output:B[0, ..., n-1], sorting of A
//Aux storage C[0, ..., K)
void CountSort(int A[], int B[], int n)
{
int *C = new int[K];
int i, j;
//初始化
memset(C, 0, sizeof(int)*K);
//统计
for (i = 0; i < n; i++)
C[A[i]]++; //C[i] = |{key=i}|
for (i = 1; i < K; i++)
C[i] += C[i - 1]; //C[i] = |{key <= i}|
//分配
for(j = n - 1; j >= 0; j--)
//for(j = 0; j < n; j++)
{
//B[C[A[j]] - 1] = A[j];
//C[A[j]]--;
B[--C[A[j]]] = A[j]; //对于每一个A[j]来说,C[A[j]]-1就是A[j]在输出数组中的最终位置,因为共有C[A[j]]个元素小于或等于A[j]
}
delete []C;
}
参考算法导论p108~110。
用于年龄排序(剑指offer—p65):
void SortAges(int ages[], int length)
{
if (ages == NULL || length <= 0)
return;
const int oldestAge = 99;
int timeOfAges[oldestAge + 1];
memset(timeOfAges, 0, sizeof(timeOfAges));
for (int i = 0; i < length; i++)
{
int age = ages[i];
if (age < 0 || age > oldestAge)
throw new std::exception("age out of range.");
timeOfAges[age]++;
}
int index = 0;
for (int i = 0; i <= oldestAge; i++)
{
for (int j = 0; j < timeOfAges[i]; j++)
{
ages[index++] = i;
}
}
}
上述两个函数最后部分不同,但功能相同。
可以利用计数排序的思想,在O(1)的时间复杂度内计算介于0~k之间的n个整数中有多少个落在区间[a,b]内(算法导论p110题8.2-4):
int CountNumberBetweenAB(int L[], int n, int a, int b)
{
const int K = 100;
int *c = new int[K];
int i;
memset(c, 0, sizeof(int)*K);
for (i = 0; i < n; i++)
c[L[i]]++;
for (i = 1; i < K; i++)
c[i] += c[i - 1];
int count = (a == 0 ? c[b] : c[b] - c[a - 1]);
delete c;
return count;
}
二、基数排序
稳定、按最低有效位到最高有效位进行排序(中间采用的是稳定呢的计数排序), 当输入的元素是n 个d位整数时,它的运行时间是 O(d(n + k)),k一般是10,空间复杂度是O(n)。
void RadixSort(int A[], int n, int d)
{
const int K = 10;
int i, j;
int *digitI = new int[n], c[K];
int *tmp = new int[n];//临时数组
int base = 1;
for (i = 0; i < d; i++)
{
memset(c, 0, sizeof(int)*K);
for (j = 0; j < n; j++)
{
digitI[j] = (A[j] / base) % K; //从低位到高位取每一位
c[digitI[j]]++;
//printf("%3d ", digitI[j]);
}
//cout << endl;
for (j = 1; j < K; j++)
c[j] += c[j - 1];
for (j = n - 1; j >= 0; j--)
tmp[--c[digitI[j]]] = A[j]; // = digitI[j]]
//for (j = 0; j < n; j++)
// printf("%03d ", tmp[j]);
//cout << endl;
memcpy(A, tmp, sizeof(int)*n);
base *= 10;
}
memcpy(A, tmp, sizeof(int)*n);
delete []tmp;
delete []digitI;
}
参考算法导论p110~112
三、桶排序
桶排序假设输入是由一个随机过程产生,该过程将元素均匀、独立地分布在[0,1)区间上。桶排序是稳定的、期望时间复杂度是O(n),空间复杂度是O(k*n/k) = O(n);
/*initial arr*/
void InitialArr(double *arr,int n)
{
srand((unsigned)time(NULL));
for (int i = 0; i < n;i++)
{
arr[i] = rand() / double(RAND_MAX+1); //[0.1)
}
}
void BucketSort(double arr[], int n)
{
const int K = 10;//桶的个数
int i, j, k;
double *bucket[K];
for (i = 0; i < K; i++)
bucket[i] = new double[n];
int count[K] = {0};//每一个桶中的元素个数
for (i = 0; i < n; i++)
{
double tmp = arr[i];
int digitI = (int)(tmp*10); //digitI表示第i个元素的小数点第一位
//小数点第一位(digitI)相同的元素放在同一个桶(bucket[digitI])中的第count[digitI]个位置上
//通过插入排序将tmp插入到有序的bucket[digitI][0,...,count[digitI] - 1]
for (j = count[digitI] - 1; j >= 0 && tmp < bucket[digitI][j]; j--)
bucket[digitI][j + 1] = bucket[digitI][j];
bucket[digitI][j + 1] = tmp;
count[digitI]++;
}
//重新连接数据
k = 0;
for (i = 0; i < K; i++)
for (j = 0; j < count[i]; j++)
arr[k++] = bucket[i][j];
for (i = 0; i < K; i++)
delete []bucket[i];
}
参考算法导论p112~114