10大排序算法之七:计数排序【稳定】,复杂度小,不常用计数排序,除非面试官特殊申明

10大排序算法之七:计数排序【稳定】,复杂度小,不常用计数排序,除非面试官特殊申明

提示:整个算法界,一共有十大排序算法,每一个算法都要熟悉,才算是算法入门

算法界的十大排序算法分别是:
选择排序、冒泡排序、插入排序、堆排序、希尔排序、归并排序、快速排序、桶排序、计数排序,基数排序
(1)选择排序:10大排序算法之一:选择排序【不稳定】,一般不用选择排序的
(2)冒泡排序:10大排序算法之二:冒泡排序【稳定的】,但复杂度高,一般不用冒泡排序的
(3)插入排序:10大排序算法之三:插入排序【稳定的】,复杂度高,系统常在数据少时用插入排序。
(4)归并排序:10大排序算法之四:归并排序【稳定的】,复杂度中,系统常用归并排序
(5)快速排序:10大排序算法之五:快速排序【不稳定】,复杂度中,系统常用快速排序
(6)堆排序:10大排序算法之六:堆排序【不稳定】,复杂度中,不常用堆排序,堆结构更重要

在这里插入图片描述
如果你想进互联网大厂,至少这上面的其中最重要的8种,是必须熟练掌握的:
去掉希尔排序,希尔排序是一种改进的插入排序算法——所以只需要掌握插入排序
桶排序中最重要的就是计数排序和基数排序,都是桶的思想
在这里插入图片描述
根据算法复杂度低一点的,又稳定的
咱们可以最常用的算法实际上就四种:
插入排序(o(n^2))【当数据量小时,这个方法简单】【稳定】、
堆排序o(nlog(n))【不稳定】、
归并排序o(nlog(n))【稳定】,
快速排序o(nlog(n))(虽然快排不稳定,但是很多不需要稳定情况下,快排非常快)
因此,o(n)的桶排序很少用,除非面试官特别申明,否则都用比较排序。
归并排序是最常用的,复杂度低,而且稳定,达到了一个非常好的折中。


题目

请你用计数排序算法,将arr排序为升序


一、审题

示例:arr = 5 3 1 8 6 2 4
让其最终变为:arr= 1 2 3 4 5 6 8


二、计数排序什么情况下用?

计数,自然就是统计词频,词频范围不可太大,如果你统计的词频范围太大,这个计数就会太复杂耗时!
统计谁?
统计arr中国每个元素的词频
找到arr最大值,范围不要太大,就可以用计数排序

词频范围不大的统计:
(1)比如让你记录班上的学生年龄,把这些年龄排序放好。【0–100岁】
班上有6个学生,年龄分别是11 13 10 12 15 10
则排序后就是10 10 11 12 13 15

(2)比如让你把班上同学的高考成绩拿出来,然后按照成绩排序。【0–750分】
班上有5个学生,成绩分别是511 413 410 412 412 315
则排序后就是315 410 412 412 413 511

如果范围太大了,比如上万啥的,就算了吧!

三、计数排序的核心思想:也是桶排序的意思

什么是桶?
逻辑上是一个桶,装很多东西,比如数
(1)排序:arr=11 13 10 12 15 10
咱们这样做,将10 放一个桶里面,计数为2,将11放一个桶,计数1,……
这样的话
在这里插入图片描述
(2)令桶为count数组,下标i就是arr中的元素,arr中最大值是15,所以count的下标i从0–15
这样,遍历arr,遇到哪个数,就去count的下标那个数计数++,代表在i这桶里多加一个数i
——这个count干了啥呢,每个位置i,就是记录了arr中有几个i?

(3)然后,我们要排序

从每一个count的下标i开始,i=0到max=15,每一个数i,咱桶中有几个i呢?
count[i]有几个,咱就需要在arr中放几个i,因为count记录的就是arr中有几个i
这样的话,从左往右放,左边小,右边大,自然我们就能排序好arr了
在这里插入图片描述
这就是计数排序,利用桶的思想,让count统计arr中每个元素 i 出现了多少次?
然后按照count的每一个i位置的量【count[i]】,重新倒出来i,把i倒进arr【倒多少次?count[i]次】
这样就把arr排序好了。

显然,这个桶的思想,往往是统计词频,它不需要咱们想前面6大算法那样,去比较两个位置,然后交换他们。
故这是不基于比较的算法,很简单,也没法考验学生的算法水平。
除非考试时面试官特别申明,咱们就用基于比较的排序,别用桶的思想。

如果从左往右统计词频时,咱们让桶,先进先出,排序的时候就能做到相对位置不变,故计数排序是稳定的

每次操作都是遍历一遍arr[i],至少o(n)复杂度
最后遍历count【范围不大的】
所以计数排序复杂度时o(n)

手撕代码:就是统计词频到桶count中,然后根据桶count倒出来,排序好了就

//复习计数排序:
    public static void sortCountReview(int[] arr){
        if (arr == null || arr.length < 2) return;

        //找到arr最大值,范围不要太大
        int max = Integer.MIN_VALUE;
        int N = arr.length;
        for (int i = 0; i < N; i++) {
            max = Math.max(max, arr[i]);
        }

        //这个范围是count桶的下标范围
        int[] count = new int[max + 1];//0--max
        //扔桶
        for (int i = 0; i < N; i++) {
            count[arr[i]]++;
        }

        //倒出来,排序,i位置有几个,那就是倒几个
        int index = 0;//倒出count每个位置i,i去覆盖arr,index做arr的下标索引
        for (int i = 0; i <= max; i++) {
            //桶里,有很多,就要连续倒
            while (count[i] > 0) {
                arr[index++] = i;//i就是我们桶代表的数,arr那来的
                count[i]--;//倒一个减一个
            }
        }
        //此时arr已经排序好了
    }

测试一把:

//对数器之构建随机数组
    public static int[] createArray(int arrSize, int maxValue){
        int[] arr = new int[arrSize];
        for (int i = 0; i < arrSize; i++) {
            arr[i] = (int)(maxValue * Math.random());//0-N-1的随机数
        }
        return arr;
    }

    public static void checker(){
        //生成检验数组
        int[] arr = createArray(10000,750);
        int[] arr2 = new int[arr.length];//赋值同样一个数组arr
        for (int i = 0; i < arr.length; i++) {
            arr2[i] = arr[i];//copy即可
        }
        int[] arr3 = new int[arr.length];//赋值同样一个数组arr
        for (int i = 0; i < arr.length; i++) {
            arr3[i] = arr[i];//copy即可
        }

        //绝对的正确方法——暴力方法,或系统函数,操作arr
        Arrays.sort(arr);
        //优化方法,操作arr2
        countSort(arr2);
        //复习优化方法,操作arr3
        countSort(arr3);

        //然后两个数组对位校验
        boolean isSame = true;
        for (int i = 0; i < arr.length; i++) {
            if(arr[i] != arr2[i]) {
                isSame = false;
                break;//只要有一个不等,不必继续判断了
            }
        }
        System.out.println(isSame == false ? "oops,wrong!" : "right!");
        isSame = true;
        for (int i = 0; i < arr3.length; i++) {
            if(arr[i] != arr3[i]) {
                isSame = false;
                break;//只要有一个不等,不必继续判断了
            }
        }
        System.out.println(isSame == false ? "oops,wrong!" : "right!");
    }

    public static void main(String[] args) {
        checker();
    }

结果:

right!
right!


总结

提示:重要经验:

1)除非面试官特殊申明,否则你不要用这种非比较的排序算法,要用基于比较的排序算法。
2)计数排序要求,arr中的元素范围不要太大,太大就没法统计了。

  • 3
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

冰露可乐

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值