稳定的排序算法(二)

基数排序(radix sort)
基数排序(radix sort)属于“分配式排序”(distribution sort),基数排序法又称“桶子法”(bucket sort)或bin sort,顾名思义,它是透过键值的部份资讯,将要排序的元素分配至某些“桶”中,藉以达到排序的作用,基数排序法是属于稳定性的排序,其时间复杂度为O (nlog(r)m),其中r为所采取的基数,而m为堆数,在某些时候,基数排序法的效率高于其它的比较性排序法。
实现方法有两种,分别如下:
最高位优先(Most Significant Digit first)法,简称MSD法:先按k1排序分组,同一组中记录,关键码k1相等,再对各组按k2排序分成子组,之后,对后面的关键码继续这样的排序分组,直到按最次位关键码kd对各子组排序后。再将各组连接起来,便得到一个有序序列。
最低位优先(Least Significant Digit first)法,简称LSD法:先从kd开始排序,再对kd-1进行排序,依次重复,直到对k1排序后便得到一个有序序列。
标准C语言实现的算法(注意:当数据中有0时,算法失效):

#include <stdlib.h>
#include <stdio.h>
#include <math.h>

void bucketSort(int *p , int n);
int  getLoopTimes(int num);
int findMaxNum( int *p , int n);
void sort(int *p , int n , int loop);

int main()
{
  int i;
  int arr[] = {5, 8, 9, 10, 2, 3, 4, 6, 7, 1};
  bucketSort(arr, 10);
  for(i = 0; i < 10; i++)
      printf("%4d", arr[i]);

  getchar();
	  
  return 0;
}

void bucketSort(int *p , int n)
{
	//获取数组中的最大数
	int maxNum = findMaxNum( p , n );
	//获取最大数的位数,次数也是再分配的次数。
	int loopTimes = getLoopTimes(maxNum);
	int i ;
	//对每一位进行桶分配
	for( i = 1 ; i <= loopTimes ; i++) 
	{
		sort(p , n , i );
	}
}

//获取数字的位数
int  getLoopTimes(int num)
{
	int count = 1 ;
	int temp = num/10;
	while( temp != 0 ) 
	{
		count++;
		temp = temp / 10;
	}
	return count;
}

//查询数组中的最大数
int findMaxNum( int *p , int n)
{
	int i ;
	int max = 0;
	for( i = 0 ; i < n ; i++)
	{
		if(*(p+i) > max) 
		{
			max = *(p+i);
		}
	}
	return max;
}

//将数字分配到各自的桶中,然后按照桶的顺序输出排序结果
void sort(int *p , int n , int loop)
{
	//建立一组桶  此处的20是预设的  根据实际数情况修改
	int buckets[10][20] = {0};
	//求桶的index的除数
	//如798   个位桶index = ( 798 / 1 )  % 10 = 8
	//        十位桶index = ( 798 / 10 ) % 10 = 9
	//        百位桶index = ( 798 / 100 )  % 10 = 7
	//  tempNum 为上式中的1、10、100
	int tempNum = (int) pow(10 , loop-1);
	int i , j ;
	int k;
	
	for( i = 0 ; i < n ; i++ ) 
	{
		int  row_index = (*(p+i) / tempNum) % 10;
		for(j = 0 ; j < 20 ; j++) 
		{
			if(buckets[row_index][j] == 0) 
			{
				buckets[row_index][j]  =  *(p+i) ;
				break;
			}
		}
	}
	k = 0;
	//将桶中的数,倒回到原有数组中
	for(i = 0 ; i < 10 ; i++) 
	{
		for(j = 0 ; j < 20 ; j++) 
		{
			if(buckets[i][j] != 0) 
			{
				*(p + k ) = buckets[i][j] ;
				buckets[i][j]=0;
				k++;
			}
		}
	}
}



鸽巢排序(Pigeonhole sort)
鸽巢排序(Pigeonhole sort), 也被称作基数分类, 是一种时间复杂度为O(n)且在不可避免遍历每一个元素并且排序的情况下效率最好的一种排序算法. 但它只有在差值(或者可被映射在差值)很小的范围内的数值排序的情况下实用。
当涉及到多个不相等的元素, 且将这些元素放在同一个"鸽巢"的时候, 算法的效率会有所降低.为了简便和保持鸽巢排序在适应不同的情况, 比如两个在同一个存储桶中结束的元素必然相等
我们一般很少使用鸽巢排序, 因为它很少可以在灵活性, 简便性, 尤是速度上超过其他排序算法. 事实上, 桶排序较鸽巢排序更加的实用。
鸽巢排序的一个比较有名的变形是tally sort, 它仅仅适用非常有限的题目, 这个算法因在Programming Pearls一书中作为解决一个非常规有限集问题方法的例子而著名.
显然, 快速排序可以当作只有两个(有些情况下是三个)"鸽巢"的鸽巢排序。
算法分析
1 给定一个待排序数组,创建一个备用数组(鸽巢),并初始化元素为0,备用数组的索引即是待排序数组的值。
2 把待排序数组的值,放到“鸽巢”里(即用作备用数组的索引)。
3 把鸽巢里的值再依次送回待排序数组。
标准C语言实现的算法

#include <stdlib.h>
#include <stdio.h>

#define NUM 100

void pigeonholeSort(int* array, int length)
{
	int auxiliary[NUM] = {0};
	int i, k,j = 0;
	for(i = 0; i < length; ++i)
	{
		auxiliary[array[i]]++;
	}

	for(i = 0; i < NUM; ++i)
	{
		for(k = 0; k < auxiliary[i]; ++k)
		{
			array[j++] = i;
		}
	}
}

int main()
{
	int i;
	int arr[] = {5, 8, 9, 10, 2, 3, 4, 6, 7, 1};
	pigeonholeSort(arr, 10);
	for(i = 0; i < 10; i++)
		printf("%4d", arr[i]);

	getchar();

	return 0;
}


备注:参考维基百科和百度百科。


猜你喜欢

转载自blog.csdn.net/lightlater/article/details/10191873