计数排序
计数排序的基本思想是对于给定的输入序列中的每一个元素x,确定该序列中值小于x的元素的个数(此处并非比较各元素的大小,而是通过对元素值的计数和计数值的累加来确定)。一旦有了这个信息,就可以将x直接存放到最终的输出序列的正确位置上。例如,如果输入序列中只有17个元素的值小于x的值,则x可以直接存放在输出序列的第18个位置上。当然,如果有多个元素具有相同的值时,我们不能将这些元素放在输出序列的同一个位置上,因此,上述方案还要作适当的修改。
步骤:
1.找到数据中的最大值max和最小值min
2.统计数组中每个值为min+i-1的元素出现的次数,存入数组arr的第i项,即a[0]记录的是min出现的次数;
比如一串数值{1,3,5,8,5,8,6,9,1}
max为9,min为1,所以构建n=max-min+1=9大小的数组arr[9],min(即1)出现2次,所以a[0]=2,min+2(即3)出现1次,所以a[2]=1,min+1出现0次,所以a[1]=0,以此类推。这样每一个数出现的次数都在数组中反应出来
3.统计完后,从arr[0]开始,依次将arr[i]对应的数值放入新数组,比如arr[0]=2,arr[1]=0,arr[2]=1,min=1,则先将arr[0]所对应min放入数组得{1,1},arr[1]没有对应元素,arr[2]=1对应一个min+2,继续放入数组得{1,1,3}。
c++实现如下:
void counting_sort(int a[], int n)//计数排序
{
int min=INT32_MAX,max=-1;
for (int i = 0; i < n; ++i) //找最大最小值
{
if (a[i] < min)
{
min = a[i];
}
if (a[i] > max)
{
max = a[i];
}
}
int len = max - min + 1; //数组长度
int* arr = new int[len]();
for (int i = 0; i < n; ++i)
{
++arr[a[i] - min]; //记录数据
}
int j = 0;
for (int i = 0; i < len; ++i)
{
while (arr[i] > 0)
{
a[j++] = i+min; //赋值到新数组(这里重新对a赋值)
--arr[i]; //每赋值一个arr[i]减1
}
}
}
时间复杂度为O(n+k),空间复杂度为O(n),适合数据范围跨度较小的排序。
桶排序
假设输入数据服从均匀分布,将数据分到有限数量的桶里,每个桶再分别排序(有可能再使用别的排序算法或是以递归方式继续使用桶排序进行排。
1:人为设置一个BucketSize,作为每个桶所能放置多少个不同数值(例如当BucketSize==5时,该桶可以存放{1,2,3,4,5}这几种数字,但是容量不限,即可以存放100个3);
2:遍历输入数据,并且把数据一个一个放到对应的桶里去
3:对每个不是空的桶进行排序,可以使用其它排序方法,也可以递归使用桶排序;
4:从不是空的桶里把排好序的数据拼接起来。
如下图(这里BucketSize为10)
c++实现如下:
int maxbit(int a[], int n) //辅助函数,求数据的最大位数
{
int d = 1; //保存最大的位数
int p = 10;
for (int i = 0; i < n; ++i)
{
while (a[i] >= p)
{
p *= 10;
++d;
}
}
return d;
}
vector<int> bucket_Sort(vector<int>arr, int n,int d) //返回桶排序后的数组
{
if (d == 0||n==0)
{
return arr;
}
int s = pow(10, d - 1);
vector<int>b[10]; //创建10个桶
vector<int>a;
for (int i = 0; i < n; i++)
{
int k = arr[i] / s;
k = k % 10; //先除再取余得到应放入的桶
b[k].push_back(arr[i]); //放入对应桶
}
for (int i = 0; i < 10; ++i)
{
int loc = 0;
int size = b[i].size();
b[i]=bucket_Sort(b[i], size,d-1); //递归使用桶排序,也可直接使用其他比较排序
for(int j=0;j<size;j++)
{
a.push_back(b[i][loc++]);
}
}
return a;
}
void bucket_sort(int a[], int n) //桶排序,这里block为10
{
int d = maxbit(a, n); //求最大位数
vector<int>arr(a, a + n); //为了避免链表的使用这里用了vecotr
vector<int> b = bucket_Sort(arr, arr.size(),d); //对arr使用桶排序
memcpy(a, &b[0], b.size() * sizeof(int)); //vector转为普通数组
}
时间复杂度为O(n+k),空间复杂度为O(n+k),k为BucketSize大小,适合数据分布均匀的排序。
基数排序
基数排序是按照低位先排序,然后收集;再按照高位排序,然后再收集;依次类推,直到最高位。有时候有些属性是有优先级顺序的,先按低优先级排序,再按高优先级排序。最后的次序就是高优先级高的在前,高优先级相同的低优先级高的在前。基数排序基于分别排序,分别收集,所以是稳定的。
基数排序的方式可以采用LSD(Least significant digital)或MSD(Most significant digital),LSD的排序方式由键值的最右边开始,而MSD则相反,由键值的最左边开始。
步骤1:取得数组中的最大数,并取得位数;
步骤2:a为原始数组,从最低位开始取每个位组成radix数组;
步骤3:对radix进行计数排序(利用计数排序适用于小范围数的特点);
第一步
以LSD为例,假设原来有一串数值如下所示:
73, 22, 93, 43, 55, 14, 28, 65, 39, 81
首先根据个位数的数值,在走访数值时将它们分配至编号0到9的桶子中:
0
1 81
2 22
3 73 93 43
4 14
5 55 65
6
7
8 28
9 39
第二步
接下来将这些桶子中的数值重新串接起来,成为以下的数列:
81, 22, 73, 93, 43, 14, 55, 65, 28, 39
接着再进行一次分配,这次是根据十位数来分配:
0
1 14
2 22 28
3 39
4 43
5 55
6 65
7 73
8 81
9 93
第三步
接下来将这些桶子中的数值重新串接起来,成为以下的数列:
14, 22, 28, 39, 43, 55, 65, 73, 81, 93
c++实现如下:
int maxbit(int a[], int n) //辅助函数,求数据的最大位数
{
int d = 1; //保存最大的位数
int p = 10;
for (int i = 0; i < n; ++i)
{
while (a[i] >= p)
{
p *= 10;
++d;
}
}
return d;
}
void radix_sort(int a[], int n) //基数排序
{
int d = maxbit(a, n);
int *tmp = new int[n];
int *count = new int[10]; //计数器
int radix=1;
for (int i = 1; i <= d; i++)
{
for (int j = 0; j < 10; j++)
{
count[j] = 0;
}
for (int j = 0; j < n; j++)
{
int k = (a[j] / radix) % 10;
count[k]++; //计数
}
for (int j = 1; j < 10; j++)
{
count[j] = count[j] + count[j - 1]; //tmp[j]为基数小与等于j的个数
}
for (int j = n - 1; j >= 0; --j)
{
int k = (a[j] / radix) % 10;
tmp[count[k] - 1] = a[j];
count[k]--; //这里使用--可以使赋值完毕之后count都为0,而不需要清0
}
for (int j = 0; j < n; ++j)
{
a[j] = tmp[j];
}
radix *= 10;
}
delete[]tmp;
delete[]count;
}
时间复杂度为O(n*k),空间复杂度为O(n+k),k为最大位数,适合位数较小的排序。
参考:百度百科