一、计数排序
基本思想:计数排序的思想和桶排序类似。对于一个给定的序列,统计出序列中比每个数小的个数,就能知道这个数最终在序列中的位置。比如序列[2, 5, 3, 0, 2, 3, 0, 3],序列中比5小的数有7个,那么5在最终的排序序列中就位于第8位(下标为7)。计数排序适用于最大值与最小值相差不大的整数序列的排序。它比快速排序更快,但是不具有普遍适用性。
排序过程:现有一个长度为n的待排序整数序列,序列中的数的范围为0~5。最简单的计数排序过程如下图
这样的方法虽然能够得到正确排序后的序列,但是它存在一个问题。比如最后的序列中有两个2,我们不知道这两个2的初始下标的前后位置。这不满足稳定性的要求,进行适当改进即可。
1、在上图中的统计之后再进行一次统计,将统计数组(C)的值改为记录中小于当前下标所代表的数的记录的总和(即C'[i]=sum(C[0],C[1],...,C[i]))。
2、按倒序遍历原始待排序序列,其值对应的统计数组的值即为该记录在最终序列中的位置。找到位置后,将统计数组对应值减1。这样,遍历到相同的值时,它在最终序列中的下标就在现在这个位置之前,满足稳定性的要求。
其过程图示如下:
稳定计数排序的C++代码实现:
#include<iostream>
using namespace std;
int * countSort(int *array, int k, int length)
{
//length-数组大小用于构造B数组
int *C = new int[k + 1];//构造C数组
for (int i = 0; i < k + 1; i++)//初始化C数组
{
C[i]=0;
}
int sum = 0;
int *B = new int[length];//构造B数组
for (int i = 0; i < length; i++)
{
C[array[i]] += 1;// 统计A中各元素个数,存入C数组
}
for (int i = 0; i < k + 1; i++)//修改C数组
{
sum += C[i];
C[i] = sum;
}
for (int i = length - 1; i >= 0; i--)//遍历A数组,构造B数组
{
int a = array[i];
int b = C[a]-1;
B[C[array[i]]-1] = array[i];//将A中该元素放到排序后数组B中指定的位置
C[array[i]]--;//将C中该元素-1,方便存放下一个同样大小的元素
}
return B;//将排序好的数组返回,完成排序
}
int main()
{
int length = 8;
int *A = new int[8] {2, 5, 3, 0, 2, 3, 0, 3};
cout << endl << "排序前:";
for (int i = 0; i < length; i++)
{
cout << A[i]<<" ";
}
int *B = countSort(A, 5,length);
cout <<endl<< "排序后:";
for (int i = 0; i < length; i++)
{
cout << B[i]<<" ";
}
system("pause");
return 0;
}
时间复杂度:Ο(n+k)
空间复杂度:Ο(n+k) 存储结果数组和统计数组
进一步改进:前面的待排序序列范围都是从0开始的,所以统计数组的长度为最大值+1。但是,如果倒排序序列的范围为90到99,如果把统计数组的长度设置为100,就会造成极大的空间和时间的浪费。可以设置一个等于最小值的偏移量来解决这个问题,使得统计数组的长度=最大值-最小值+1。
改进后的C++代码如下:
#include<iostream>
using namespace std;
int * countSort2(int *array, int length)
{
//length-数组大小用于构造B数组
//先遍历一遍找出序列中的最小值和最大值
int min = 0;
int max = 0;
for (int i = 0; i < length; i++)
{
if (array[i] > max) max = array[i];
if (array[i] < min) min = array[i];
}
int k = max - min + 1;//统计数组的长度k
int *C = new int[k + 1];//构造C数组
for (int i = 0; i < k + 1; i++)//初始化C数组
{
C[i] = 0;
}
int sum = 0;
int *B = new int[length];//构造B数组
for (int i = 0; i < length; i++)
{
//最小值作为偏移量
C[array[i]-min] += 1;// 统计A中各元素个数,存入C数组
}
for (int i = 0; i < k + 1; i++)//修改C数组
{
sum += C[i];
C[i] = sum;
}
for (int i = length - 1; i >= 0; i--)//遍历A数组,构造B数组
{
B[C[array[i]-min] - 1] = array[i];//将A中该元素放到排序后数组B中指定的位置
C[array[i]-min]--;//将C中该元素-1,方便存放下一个同样大小的元素
}
return B;//将排序好的数组返回,完成排序
}
int main()
{
int length = 8;
int *A = new int[8] {92, 95, 93, 90, 92, 99, 90, 93};
cout << endl << "排序前:";
for (int i = 0; i < length; i++)
{
cout << A[i]<<" ";
}
int *B = countSort2(A,length);
cout <<endl<< "排序后:";
for (int i = 0; i < length; i++)
{
cout << B[i]<<" ";
}
system("pause");
return 0;
}
二、拓扑排序