算法介绍——桶排序

桶排序

桶排序是最快最简单的排序算法。

举例说明

假设班上有 5 个学生,这 5 个学生分别考了 5分、3分、5分、2分和 8分(满分是10分 ^_^)。接下来将分数按从大到小进行排序,排序后是 8 5 5 3 2。

如何编写一段程序,让计算机随机读入 5 个数然后将这 5 个数从大到小输出?

只需要借助一个一维数组就可以解决这个问题。

  • 首先我们需要申请一个大小为 11 的数组 int a[11],将 a[0]~a[10] 都初始化为 0,表示这些分数还都没有人得过。比如,a[0] 等于 0 就表示目前还没有人得过 0 分,a[10] 等于 0 表示目前还没有人得过 10 分。
  • 接着处理每个人的分数,第一位学生的分数是 5 分,所以我们将对应的 a[5] 的值在原来的基础增加 1,即将 a[5] 的值从 0 改为 1,表示 5 分出现过一次。
  • 第二位学生的分数是 3 分,所以我们将对应的 a[3] 的值在原来的基础增加 1,即将 a[3] 的值从 0 改为 1,表示 3 分出现过一次。
  • 需要注意的是,第三位学生的分数也是 5 分,所以 a[5] 的值需要在其基础上再增加 1,即将 a[5] 的值从 1 改为 2,表示 5 分出现过两次。
  • 以此类推,第四和第五位学生的分数将在 a[2]a[8] 中体现出来。

整个过程如下图所示:

在这里插入图片描述

发现没有?

a[0]~a[10] 中的数值其实就是 0 分到 10 分每个分数出现的次数。因此,我们只需要将出现的分数打印出来就达到排序的目的了。

C代码实现

#include <stdio.h>

#define BUCKET_SIZE 11
#define STUDENT_CNT 5

int main(void)
{
	int a[BUCKET_SIZE] = {0};
	int i, j, t;

	/* 循环读入5个数 */
	for(i=0; i<STUDENT_CNT; i++) {
		scanf("%d", &t);  /* 把每一个数读到变量t中 */
		a[t]++;           /* 进行计数 */
	}

	/* 打印数组的值 */
	for(i=0; i<BUCKET_SIZE; i++) {
		printf("%d ", a[i]);
	}
	printf("\n");
	
	/* 将分数从大到小打印输出 */
	for(i=BUCKET_SIZE-1; i>=0; i--) {
		for(j=0; j<a[i]; j++) {
			printf("%d ", i);
		}
	}
	printf("\n");

	return 0;
}

编译并执行代码,如下:

在这里插入图片描述

可以看到,数组的值 0 0 1 1 0 2 0 0 1 0 0 和我们前面的分析是一样的,并对 5 个学生的分数按由大到小进行排序,即 8 5 5 3 2

这个算法就好比有 11 个桶,编号从 0~10。每出现一个数,就在对应编号的桶中放一个小旗子,最后只要数一下每个桶中有多少个小旗子即可。比如 2 号桶中有 1 个小旗子,表示 2 出现了一次;5 号桶中有 2 个小旗子,表示 5 出现了两次;10 号桶中没有小旗子,表示 10 出现了零次。

在这里插入图片描述

这种排序方法我们暂且称为 “桶排序”。其实真正的桶排序要比这个复杂一些,以后再详细讨论。

更进一步,我们可以尝试输入 n 个 0~1000 之间的整数,将它们从大到小排序。显然此时需要 1001 个桶,用来表示 0~1000 之间每个数出现的次数。

复杂度分析

在上述代码中,忽略 “打印数组的值” 块循环,假设前面读入待排序数时循环了 n 次,后面排序时一共循环了 m+n 次(n 为待排序的个数,m 为桶的个数)。用大写字母 O 表示时间复杂度,该算法的时间复杂度为 O ( n + m + n ) \rm{O(n+m+n)} ,即 O ( 2 n + m ) \rm{O(2n+m)} 。忽略较小的常数,最终桶排序法的时间复杂度可以表示为 O ( M + N ) \rm{O(M+N)}

桶排序是一个非常快的排序算法。但显然,如果需要排列的数值范围很大,就需要创建非常大的数组,因此在内存资源紧缺的场合就不太合适了。

发布了299 篇原创文章 · 获赞 1219 · 访问量 159万+

猜你喜欢

转载自blog.csdn.net/luckydarcy/article/details/102461520