计数排序 [数据结构与算法][Java]

计数排序

计数排序和基数排序都是桶排序的一种应用

适用场景:

量大但是范围小

  • 比如对10000个数进行排序, 但是这10000个数中只有10种数字(0 - 9)
  • 典型题目:
    1. 某大型企业数万名员工年龄排序
    2. 如何快速得知高考名次(腾讯面试)

这里我们以某大型企业数万名员工年龄排序来进行一个讲解:

假设有两万名员工, 员工的年龄在[0 - 60]之间, 那么如何用计数排序完成排序操作?

  1. 首先, 肯定是有一个原始数组, 长度为20万, 其中是放的每个员工的年龄, 我们先对这个原始数组进行一次遍历
  2. 创建一个新的数组(计数数组), 这个数组中对应下标位置放的就是该年龄的人员的数量
    • 例如: 如果是下标为10的位置放的就是10岁的员工的数量
  3. 遍历原数组的过程中, 统计对应年龄的人数(也就是完善计数数组)
  4. 创建一个新的数组(结果数组), 结果数组的长度与原始数组的长度相同
  5. 遍历计数数组, 在遍历的过程中完成在结果数组中值的添加
    • 例如: 计数数组中0有3个, 那么在结果数组中添加3个0, 然后1有4个, 再添加4个1, 一直添加到遍历完技术数组为止

做题理解:

问题:
一个数组中的数值都是0 - 9构成, 现在让你对该数组中数值进行一个升序排序:
算法图解:

在这里插入图片描述

代码:
package com.ffyc.algorithm.计数排序;

import java.util.Arrays;

public class CountSort {
    static int [] sort(int [] arr){
        int [] result = new int[arr.length];
        int [] count = new int[10];

        for(int i = 0; i < arr.length; i++){
            count[arr[i]]++;
        }

        for(int i = 0,j = 0; i < count.length; i++){
            while(count[i]-- > 0) result[j++] = i;
        }

        return result;
    }

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

        int[] sort = sort(arr);

        System.out.println(Arrays.toString(sort));
    }
}

算法优化:

我们可以发现: 基于上面我们设计的计数排序算法, 有一个很大的问题, 那就是这个算法是一个不稳定的排序算法,那么我们要如何解决这一问题? 也就是如何使得我们的计数排序算法成为一个稳定的排序算法?

  • 不稳定 : 排序后元素的相对位置会改变

我们通过一个案例来理解并解决这一不稳定问题

如果这个时候是两万个Employee类(员工类)的对象让你进行排序, 要求使用计数排序算法, 并且排序之后元素的相对位置不变, 那么如何使用计数排序做到?

我们要增加一个累加数组, 我们在原本不稳定的计数排序算法上进行一个如下修改:

  1. 在获取到计数数组之后, 不直接使用计数数组来得到结果数组, 而是使用计数数组求一个累加数组
    • 累加数组中的第i个元素值为: add[i] = count[i] + count[i - 1] + … + count[0];
      • 而我们有了累加数组之后其实原本的计数数组就没有用了, 所以我们直接在原本的计数数组上做一个修改即可:
        • 从前到后遍历一次计数数组, 使得计数数组有性质: count[i] = count[i] + count[i - 1]
  2. 在得到了累加数组之后, 我们可以发现累加数组中的每个元素其实就是对应元素在结果数组中的最后一个位置 + 1
  3. 逆序遍历一次原数组, 在遍历的过程中, 将原数组中的值放在结果数组中合理的位置
    • 什么是合理的位置?
    • 我们下面题目中会使用画图的方式来进行一个解释说明

做题理解:

问题:
一个数组中的数值都是0 - 9构成, 现在让你对该数组中数值进行一个升序排序(要求排序是稳定的):
算法图解:

在这里插入图片描述

代码:
package com.ffyc.algorithm.计数排序;

import java.util.Arrays;

public class CountSort {
    static int [] sortPlus(int [] arr){
        int [] result = new int[arr.length];
        int [] count = new int[10];

        for(int i = 0; i < arr.length; i++){
            count[arr[i]]++;
        }

        for(int i = 1; i < count.length; i++){
            count[i] = count[i] + count[i - 1];
        }

        for(int i = arr.length - 1; i >= 0; i--){
            result[count[arr[i]]-- - 1] = arr[i];
        }

        return result;
    }

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

        int[] sort = sort(arr);

        System.out.println(Arrays.toString(sort));

        int[] sortPlus = sortPlus(arr);

        System.out.println(Arrays.toString(sortPlus));
    }
}
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值