有关比较排序的算法的介绍,在这里:

 http://zhweizhi.blog.51cto.com/10800691/1786367

 



——————————————————————————————————

这里主要介绍非比较排序算法。非比较排序主要有2种算法,分别是:

计数排序(CountSort

基数排序(RadixSort)

 

一、计数排序

计数排序的实现需要借助哈希表。

思路比较清晰,注释也比较详细

这种排序的套路其实就是把每个元素映射到一个哈希表上,

然后这个哈希表自然就是排好的顺序了。


只不过有些细节要注意,

比如对于哈希表的长度应该怎么开辟合适

比如对于哈希表中为0的元素怎么处理

比如对于多个重复元素怎么处理 等……

以上细节都在代码里有比较详细的注释

下面是代码:


//非比较排序——计数排序
void CountSort(int *arr, int sz, int min, int max)
{
 assert(arr);
 assert(min < max);
 if (min == max)
 {
  return;
 }
 //开辟哈希表 和 临时数组(这个临时数组可以存放排序好的元素,最后将该数组的内容赋给arr即可)
 int *TmpList = new int[sz];
 int *CountList = new int[max - min + 1];
 memset(CountList, 0, (max - min + 1) * sizeof(int) );
 //哈希表元素初始化为全0
 for (int i = 0; i < sz; ++i)
 {
  ++CountList[arr[i] - min];
 }
 //向临时数组中存放排序好的元素
 int index = 0;
 for (int i = 0; i < max - min + 1; ++i)
 {
  while (CountList[i]-- != 0)
  {
   TmpList[index++] = i + min;
  }
 }
 //将临时数组中的内容导入到arr中
 for (int i = 0; i < sz; ++i)
 {
  arr[i] = TmpList[i];
 }
 delete[] TmpList;
 delete[] CountList;
}


二、基数排序

基数排序,用到了“哈希桶”的思想。

它的实现思路是利用哈希桶按位排序 这里可以是从低位到高位,也可以是从高位到低位

所以根据按位排序的顺序,又可以分为:

低位基数排序(LSD

高位基数排序(MSD

这里可以注意,每次排序对象的范围都是 0 ~ 9 10个元素。

下面用代码来实现低位基数排序:

 

//—————非比较排序——基数排序:

//找出一组数据中的最长位
int MaxDigit(int *arr, int sz)
{
	int max = 10;
	int digit = 1;
	for (int i = 0; i < sz; ++i)
	{
		while (arr[i] > max)
		{
			++digit;
			max *= 10;
		}
	}
	return digit;
}

//基数排序
void LSDSort(int *arr, int sz)
{
	assert(arr);
	if (sz <= 1)
	{
		return;
	}
	int StartList[10] = { 0 };
	int CountList[10] = { 0 };
	int *Backet = new int[sz];
	int Digit = MaxDigit(arr, sz);  //数组中的最长元素的位数
	int BaseDigit = 1;
	for (int i = 1; i <= Digit; ++i)
	{
	        //装入桶中
		memset(CountList, 0, sizeof(int) * 10);
		for (int i = 0; i < sz; ++i)
		{
			int num = (arr[i] / BaseDigit) % 10;
			++CountList[num];
		}
		//整理“桶”
		StartList[0] = 0;
		for (int i = 1; i < 10; ++i)
		{
			StartList[i] = CountList[i - 1] + StartList[i - 1];
		}
		//对桶中盛放的元素进行排序(方法类似计数排序)
		for (int i = 0; i < sz; ++i)
		{
			int num = (arr[i] / BaseDigit) % 10;
			int &index = StartList[num];
			Backet[index++] = arr[i];
		}
		memcpy(arr, Backet, sizeof(int) * sz);
		BaseDigit *= 10;
	}
	delete[] Backet;

}


这里举个例子来说明:(画图偷懒用的是Excel)

有一组待排序的数据:

11, 33, 51, 72, 84, 91

排序的过程大体上是这样的:


先排个位:

    将所有元素按个位的大小,映射到一个桶中:

非比较排序算法_结构

    然后按照桶号的顺序,将桶中的元素“串起来”,得到元素序列:

    11    51    91    72    33    84


再排十位:

    将上一步最后得到的数组的所有元素按十位的大小,映射到一个桶中:

    非比较排序算法_数据_02

    看,排好了。


三、总结


    这里介绍了两种非比较排序:计数排序、基数排序。

     其中,计数排序的时间复杂度是O(N),空间复杂度是O(MAX - MIN),可以看出,对于一组范围比较小的数据,它的速度非常快,而且稳定(比较排序中时间复杂度平均最快也是O(N * LgN)),但如果数据范围跨度过大,那么这种方式会浪费很多空间。

    基数排序是计数排序的升级版,用桶来代替计数排序中的辅助数组,时间复杂度为O(N * 位数) 空间复杂度为O(N)。它每趟排序都按照当前位上数据的大小对数组进行排序,而这又分为LSD和MSD两种(分别是从低位到高位;和从高位到低位)

    非比较排序的稳定性好,速度快。