基数排序
基数排序是桶排序的扩展,是非比较排序.主要思路如下:
- 把数字从低位到高位抽取出来,抽取出来相应的数值作为计数数组的下标,更新计数数组
- 并把数字放入与其对应数位相同数值的桶中,直至每个数字都操作到
- 再把桶中的数值赋值到原来的数组
- 重复上述的操作,直至轮数等于最大值的位数
表示太麻烦了,直接给图:
注意:这里省去了每次从桶中赋值到数组的操作. 实际上每次从桶中赋值后,数组是这样的:
那就开始写代码:
首先得确定轮数,轮数由最大值的位数决定,所以这个函数确定最大值的位数:
int getloop(int nums[],int len)
{
int j=0;
int nmax=nums[0];
for (int i=0;i<len;i++)
{
if (nmax<nums[i]) nmax=nums[i];
}
while (nmax>0)
{
nmax=nmax/10;
j++;
}
return j;
}
然后就是对序列排序
//传入的数字 以及求哪位的数值
int getDigit(int nums,int i)
{
return (nums/((int)pow(10,i-1)))%10;
}
void radixSort(int nums[],int left,int right)
{
int tmp[right-left+1]; //辅助数组与原数组大小一样
int count[10]; //计数数组,0-9
int loop=getloop(nums,right+1); //一共的轮数
for (int d=1;d<=loop;d++)
{
for (int i=0;i<10;i++) count[i]=0; //计数数组初始化
for (int i=left;i<=right;i++)
{
int digit=getDigit(nums[i],d);
count[digit]++; //把得到的d位的数值计数
}
for (int j=1;j<10;j++)
{
count[j]=count[j]+count[j-1]; //求出<=j的数组之和
}
//这里我们把得到的数字放入辅助数组
for (int i=right;i>=left;i--)
{
int digit=getDigit(nums[i],d); //得到nums[i]数字的第d位数值
tmp[count[digit]-1]=nums[i];
//把nums[i]的数字放入tmp数组的第 count[digit]-1位(下标-1,从0开始)
// 为什么放在count[digit]-1位:
// 我个人理解是,这是从最后开始往前找的,所以不管如何放,它的相对位置一定在最后。
count[digit]--; //拿走了一个数字,就减1
}
//把tmp赋值给原数组
for (int i=left,j=0;i<=right;i++,j++)
{
nums[i]=tmp[j];
}
}
}
完整代码:
#include <iostream>
#include <cstring>
#include <cmath>
using namespace std;
int getloop(int nums[],int len)
{
int j=0;
int nmax=nums[0];
for (int i=0;i<len;i++)
{
if (nmax<nums[i]) nmax=nums[i];
}
while (nmax>0)
{
nmax=nmax/10;
j++;
}
return j;
}
//传入的数字 以及求哪位的数值
int getDigit(int nums,int i)
{
return (nums/((int)pow(10,i-1)))%10;
}
void print(int nums,int len)
{
for (int i=0;i<len;i++) cout<<nums[i]<<" ";
cout<<endl;
}
void radixSort(int nums[],int left,int right)
{
int tmp[right-left+1]; //辅助数组与原数组大小一样
int count[10]; //计数数组,0-9
int loop=getloop(nums,right+1); //一共的轮数
for (int d=1;d<=loop;d++)
{
for (int i=0;i<10;i++) count[i]=0; //计数数组初始化
for (int i=left;i<=right;i++)
{
int digit=getDigit(nums[i],d);
count[digit]++; //把得到的d位的数值计数
}
for (int j=1;j<10;j++)
{
count[j]=count[j]+count[j-1]; //求出<=j的数组之和
}
//这里我们把得到的数字放入辅助数组
for (int i=right;i>=left;i--)
{
int digit=getDigit(nums[i],d); //得到nums[i]数字的第d位数值
tmp[count[digit]-1]=nums[i];
//把nums[i]的数字放入tmp数组的第 count[digit]-1位(下标-1,从0开始)
// 为什么放在count[digit]-1位:
// 我个人理解是,这是从最后开始往前找的,所以不管如何放,它的相对位置一定在最后。
count[digit]--; //拿走了一个数字,就减1
}
//把tmp赋值给原数组
for (int i=left,j=0;i<=right;i++,j++)
{
nums[i]=tmp[j];
}
print(nums,right);
}
}
int main()
{
int nums[]={10,162,3,34,55,65,15,78};
//cout<<getDigit(nums,sizeof(nums)/sizeof(int));
radixSort(nums,0,sizeof(nums)/sizeof(int)-1);
}
结果与我们模拟的情况也对应上了
n:表示待排序元素的个数。 k:表示待排序元素的最大值。d:表示待排序元素的位数。
时间复杂度:o(d(n+k)) 也可以认为是:o(n*k)
空间复杂度:开辟了两个数组 所以是o(k+n) n 是tmp大小 k 是计数数组大小
同时也是一种稳定的算法
桶排序
这里主要介绍它的思想方法:
- 之前的计数排序与基数排序都用到了桶排序的思想
- 桶排序:为序列分配n个桶,每个桶限定范围,把满足范围的元素加入符合的桶中
- 再在桶中对加入的元素排序
- 最后把桶中的元素拿出来,即为有序序列
复杂度分析,借用马士兵老师的总结:
同时它也是一个稳定性的算法.