算法入门(一)排序之桶排序

问题引入

当我们需要对一组数据(无序)进行排序时,假设期末考试完了,老师要按照分数对大家的名次进行排序,这里假设五个同学分别考了 5分、3分、5分、2分、8分 (满分十分)。当然,我们一下子就看出了其中的奥妙,但是我们用程序该怎么写呢?

思路引导

我们可以创建一个大小为11的数组(因为这里统计的是0到10之间的数字出现次数),现在我们有11个变量,即从a[0]~a[10],刚开始我们将数组中所有的变量a[0]~a[10]的初始值设定为0(即这些分数没有人得过)。即a[0] = 0; 代表没人得过0分,a[1] = 0; 代表没人得过1分。

接下来我们开始处理每一个人的分数,若该分数出现一次,其对应的数组下标的值加一,即第一个同学的分数为5分,代表分数5出现一次,则相对应的a[5]++; 即现在a[5] = 1。

以此类推:第二个同学的分数为3分,此时3分出现一次,其对应的数组下标的值加一,则对应的a[3]++; 即a[3] = 1。

然后我们分别拿到2分,5分,8分(第三个同学的成绩也是5分,则a[5]原来值为1的基础上再加一,a[5]++; a[5] = 2)

一路分析下来,很明确,a[0]~a[10]中的数值就是0~10中每个分数出现的次数,接下载,我们只需要根据出现过分数的次数打印数组的下标(该分数)即可,出现几次就打印几次(比如5分出现两次,a[5] = 2,则将下标5打印两次)。

代码实现

#include "stdio.h"
int main()
{
    //创建一个长度为11的数组,用来判断0-10数字出现的次数
    int arr[11], i, j, n;
    //将数组内所有下标值初始化为0,代表此数字出现0次
    for (i = 0; i <= 10; i++) {
        arr[i] = 0;
    }
    //循环读入五个0-10内的整数
    for (i = 0; i < 5; i++) {
        //将每个数读到变量n中
        scanf("%d", &n);
        //如果出现该数字,则对应的数组角标值加一
        arr[n]++;
    }
    //第一层循环依次判断arr[0]-arr[10]中的值
    for (i = 0; i <= 10; i++) {
        //第二层循环判断该数字出现的次数,有几次就打印几次
        for (j = 1; j <= arr[i]; j++) {
            printf("%d", i);
        }
    }
}

运行结果:

要想从大到小排序,将for (i = 0; i <= 10; i++)改为for (i = 10; i >= 0;  i--)即可。

解析

这个算法就好比有 11 个桶,编号从 0~10。每出现一个数,就在对应编号的桶中放一个 小旗子,最后只要数数每个桶中有几个小旗子就 OK 了。例如 2 号桶中有 1 个小旗子,表示 2 出现了一次;3 号桶中有 1 个小旗子,表示 3 出现了一次;5 号桶中有 2 个小旗子,表示 5 出现了两次;8 号桶中有 1 个小旗子,表示 8 出现了一次。

当然,我们可以想一下,这个算法是十分浪分空间的,当我们要统计要排序数的范围是 0~2100000000 之间,那你则需要申请 2100000001 个变量,也就是说要写 成 int a[2100000001]。因为我们需要用 2100000001 个“桶”来存储 0~2100000000 之间每一 个数出现的次数。即便只给你 5 个数进行排序(例如这 5 个数是 1、1912345678、2100000000、 18000000 和 912345678),你也仍然需要 2100000001 个“桶”,这真是太浪费空间了!还有, 如果现在需要排序的不再是整数而是一些小数,比如将 5.56789、2.12、1.1、3.123、4.1234 这五个数进行从小到大排序,这就显得无法实现了。

显然,简化版桶排序算法,其本质上还不能算是一个真正意义上的排序算法,其有很大的局限性,作为排序入门了解一下就可以了。

总结

简化版桶排序,用数组角标值从0自增的方式记录该角标(即输入的数字)出现的次数,然后遍历数组根据对应角标值的大小确认打印该角标的次数(即数字出现的次数)。

算法新手上车,请多指教!

原文地址:https://www.cnblogs.com/itjiangpo/p/14181233.html