排序算法 - 计数排序与桶排序

前言

经典的十大排序算法可以大致分为两类:

  • 比较类排序:
    冒泡排序 =》快速排序(冒泡排序优化)
    插入排序=》希尔排序(插入排序优化)
    选择排序=》堆排序(选择排序优化)
    归并排序(二路、多路)
  • 非比较类排序:计数排序 = 》桶排序
    基数排序
    排序算法 - 计数排序与桶排序_第1张图片

比较类排序就是需要通过比较数组中的数据来交换位置,实现有序

而非比较类,例如基数排序,就不需要比较,只用不断的分发到不同的桶,然后重新赋值给原数组,最终就可以达成有序

前面比较类排序除了堆排序都完成了,非比较类完成了基数排序
这篇将讲述计数排序、桶排序,与基数排序类似

实际上计数排序、基数排序就是特殊的桶排序,桶排序是一种排序的思想,其实现包括计数排序和基数排序两种


计数排序

计数排序:根据元素序列的最大值开辟一个容器空间,对于给定的输入序列中的每一个元素x,放到容器空间第x位置上,该位置上的值表示该数据的个数

举个栗子:

  • 一个待排序数组arr=[6,2,8,1,0,3,5,4,9,7,6],先遍历数组,最大值为9,就创建大小为10(0~9)的数组temp
  • 遍历原数组,遍历到6,temp[6]++,遍历到2,temp[2]++
  • 最终得到结果temp={1,1,1,1,1,1,2,1,1,1}
  • 就是数组temp[i],i表示原数组值,temp[i]表示i出现的次数,最后遍历temp输出的结果就是排序后的数组

可以看出,计数排序就很典型的空间换时间


Java实现计数排序

根据上面的步骤完成编程

package com.company.sort;

import java.util.Arrays;

/**
 * @author zfk
 * 计数排序
 */
public class CountingSort {

    public static void main(String[] args) {
        int[] arr = {6,2,8,1,0,3,5,4,9,7,6};
        countingSort(arr);

        System.out.println("排序后的数组:"+ Arrays.toString(arr));
    }

    public static void countingSort(int[] arr){

        //得到最大值
        int max = 0;
        for (int i = 0;i < arr.length;i++){
            if (arr[i] > max){
                max = arr[i];
            }
        }
        //创建排序的数组
        int[] temp = new int[max + 1];
        //遍历原数组
        for (int i = 0;i < arr.length;i++){
            //原数组值num为新数组的temp[num]
            int num = arr[i];
            temp[num]++;
        }
        //创建一个指向原始数组的指针
        int index = 0;
        //遍历新数组,赋值给原数组,得到有序数组
        for (int i = 0;i < temp.length;i++){
            //temp[i]的值表示有多少个大小为i的数据
            for (int j = 0;j < temp[i];j++){
                //将i 赋值给原数组,index后移
                arr[index] = i;
                index++;
            }
        }

    }
}

排序算法 - 计数排序与桶排序_第2张图片

实际上还可以进行一定的优化:找出数组的最大值、最小值,然后先数组都减去最小值,继续进行上面的计数排序,最后得到结果的时候加上最小值

就可以减小创建的数组大小,但这样又是时间换空间了,感觉是没必要的

计数排序比较简单,也比较强悍,根本不需要比较,时间复杂度为,我们可以分析一下计数排序的缺陷:

  • 处理不了小数,1.1、1.11怎么处理?
  • 处理负数需要加上最小负数的绝对值,将数组转变成正整数数组,又增加了时间
  • 当带排序数量最大最小值差距过大时,并不适用于计数排序,例如0~一千万里面只有10个数据,浪费了大量的空间,数组过大遍历也会花费很多时间
  • 空间换时间,处理不了较大数据

因为计数排序的诸多缺陷,所以就算计数排序很快很简单,使用的并不多


桶排序

桶排序是对计数排序的升级版

桶排序 (Bucket sort)或所谓的箱排序,是一个排序算法,工作的原理是将数组分到有限数量的桶子里。每个桶子再个别排序(有可能再使用别的排序算法或是以递归方式继续使用桶排序进行排序)

其实,先了解排序算法 - 基数排序图文解析实现,再看桶排序会很简单

桶排序:

  • 设置一定大小的桶,如要排序arr=[6,2,8,1,0,3,5,4,9,7,6],我们可以设置5个桶,每个桶两个单位:[0,1],[2,3]…
  • 遍历数组,放入这5个桶中
  • 桶中可以按照其他排序算法排序,也可以递归桶排序

感觉桶排序已经偏离了非比较排序

如果继续递归桶排序,就是计数排序了,如果使用了其他排序算法,就不是非比较排序了

所以说感觉桶排序是一种思想:设置桶,根据一定规则分割数据放入桶中

桶排序是一种排序的思想,其实现包括计数排序和基数排序两种

就不具体实现了,因为要涉及选择其他排序算法,关于冒泡排序、选择排序、插入排序、快速排序、希尔排序、归并排序前面已经完成了,如果要实现桶排序,直接插入函数接口即可


非比较排序

计数排序、桶排序、基数排序

非比较排序因为不是通过比较两元素而实现有序,所以需要额外的空间完成排序

非比较排序都是空间去换时间,所以容易溢出,无法处理大量的数据

总体决定,当数据量小时,且数据符号要求(正整数),使用非比较排序是很合适的,又简单又快

你可能感兴趣的:(数据结构和算法)