《算法导论》读书笔记--计数排序&基数排序

计数排序是用空间换取时间的算法,其假设n个输入元素中的每一个都在0到k区间内的一个整数,其中k为某个整数。当k=O(n)时,排序的运行时间为Theta(n)。

基本思想:对每一个输入元素x,确定小于x的元素个数。利用这一信息,就可以直接把x放到它在输出数组中的位置上了。

输入数组为A[1..n]

辅助数组:B[1..n]存放排序的输出,C[1..k]提供临时存储空间(存放A中数组元素值的出现次数)。

伪代码:

COUNTING-SORT(A,B,k)
    let C[0..k] be a new array
    for i = 0 to k //后面需要使用数组C来存储A中各元素的出现次数,所以需要清零操作
        C[i] = 0
    for j = 1 to A.length 
        C[A[j]] = C[A[j]] + 1 //统计A中各元素的出现次数存储在C中,C中下标为A中的元素值,与其对应的数组元素为出现次数,如C[1] = 0,即为“1”在A中的出现次数为0
    for i = 1 to k 
        C[i] = C[i] + C[i-1] //计算A中小于等于i的一共有多少个,方便此后将i放在数组B中正确的位置上
     for j = A.length downto 1
        B[C[A[j]]] = A[j] //使用A中元素A[j]的出现次数作为B中的下标,把每个元素放在B中适当的位置
        C[A[j]] = C[A[j]] -1 //在B中放入一个元素,与其对应C中出现的次数-1。这样,当遇到下一个等于A[j]的输入元素时,该元素可以被直接放到B中A[j]的前一位置上

   1  2 3 4 5 6 7 8                    0 1 2 3 4 5                       1 2 3 4 5 6 7 8 

A[2 5 3 0 2 3 0 3]                C[2 2 4 7 7 8]                  B[                    3   ]

    0 1 2 3 4 5                                                                     0 1 2 3 4 5

C[2 0 2 3 0 1]                                                                C[2 2 4 6 7 8]

              (a)                                      (b)                                         (c)


    1 2 3 4 5 6 7 8                     1 2 3 4 5 6 7 8                1 2 3 4 5 6 7 8

B[   0               3   ]                B[   0          3 3   ]             B[0 0 2 2 3 3 3 5]

    0 1 2 3 4 5                            0 1 2 3 4 5

C[1 2 4 6 7 8]                       C[1 2 4 5 7 8]

               (d)                                     (e)                                          (f)

步骤(a)为伪代码中第二个for循环,统计A中各元素的出现次数

步骤(b)为伪代码中第三个for循环,统计A中小于等于i元素的数量

步骤(c)(d)(e)为伪代码中第四个for循环,进行排序

步骤(f)为排序结果。


计数排序为稳定的排序算法,通常被用做基数排序算法的一个子过程,为了使基数排序能够正确运行,计数排序必须是稳定的。


计数排序Demo:

#include 
#include 
#include 

#define LENGTH 8
void counting_sort(int *A,int *B,int k)
{
	int *C=(int *)malloc(k*sizeof(int));
	for(int i = 0;i < k;i++)
		C[i] = 0;

	for(int i = 0;i < LENGTH;i++)
		C[A[i]]++;

	for(int i = 1;i < k;i++)//i应从1开始,若从0开始,则C[i-1]越界
		C[i] = C[i] + C[i-1];

	for(int i = LENGTH-1;i >= 0;i--)
	{
		B[C[A[i]]-1] = A[i];//B数组的下标从0开始,C[A[i]]中的元素数量的总和从1开始计数,所以存放到B中时需要减1,才能放到正确的位置上 想象当A中只有一个元素,C[A[i]] = 1,要存放进B中,就是B[C[A[i]]-1]
		C[A[i]]--;
	}
	free(C);
	C = NULL;
}

int main()
{
	int A[LENGTH],B[LENGTH];
	for(int i = 0;i < LENGTH;i++)
		std::cin >> A[i];
		//scanf("%d",A[i]);
	counting_sort(A,B,6);//k决定了数组中数的取值范围 0<=x<=k
	for(int i = 0;i < LENGTH;i++)
		printf("%d ",B[i]);
	printf("\n");
	system("pause");
	return 0;
}


基数排序:

算法描述参照 算法总结系列之五: 基数排序(Radix Sort) -- Jeffrey Sun

radix_sort Demo:

#include 
#include 
#include 
using namespace std;

#define LENGTH 4
#define NUM_LEN 3 //最大数位长度
void radix_sort(int *A,int d)
{
	int sort_num = 0;
	int B[LENGTH];
	for(int j = 1;j <= d;j++)//d是总位数,位数由低到高进行排序
	{
//计数排序部分
			int p = (int)pow(10,j-1);//用于A[i]/p%10 获取整个数组第j位的值,本轮是按照数组的第j位进行排序
			int C[10];//每一位的取值共有0-9十个情况
			for(int i = 0;i < 10;i++)
				C[i] = 0;

			for(int i = 0;i < LENGTH;i++)
				C[A[i]/p%10]++;

			for(int i = 1;i < 10;i++)//i应从1开始
			C[i] = C[i] + C[i-1];

			for(int i = LENGTH-1;i >= 0;i--)
			{
				B[C[ A[i]/p%10 ]-1] = A[i];//B数组的下标从0开始,C[A[i]]中的元素数量的总和从1开始计数,所以存放到B中时需要减1,才能放到正确的位置上 想象当A中只有一个元素,C[A[i]] = 1,要存放进B中,就是B[C[A[i]]-1]
				C[A[i]/p%10]--;
			}
//end
			for(int i = 0;i < LENGTH;i++)//将本轮排序结果赋值回原数组
				A[i] = B[i];
	}
}

int main()
{
	int A[LENGTH];
	for(int i = 0;i < LENGTH;i++)
		cin >> A[i];
	radix_sort(A,NUM_LEN);
	for(int i = 0;i < LENGTH;i++)
		cout << A[i] <<" ";
	cout<


你可能感兴趣的:(算法)