(十一)Java算法:计数排序(详细图解)

一、前言

1.1、概念

   计数排序:核心在于将输入的数据值转化为键存储在额外开辟的数组空间中。作为一种线性时间复杂度的排序,计数排序要求输入的数据必须是有确定范围的整数

1.2、算法步骤

  我们大概讲一下算法的步骤。

  1. 找出待排序的数组中的最大元素max和最小元素min
  2. 统计数组中每个元素num出现的次数,存入数组countArraycountArray[num-min]项中
  3. 计数数组变形,对所有的计数累加(从第一项开始countArray[i] = countArray[i] + countArray[i - 1]
  4. 反向填充目标数组arr:从后往前遍历待排序的数列copyArray(拷贝份),由数组元素num计算出对应的计数数组的索引countIndexnum - min,从而推断出numarr的位置为indexcountArray[num-min] - 1,然后num填充到arr[index],最后记得计数数组的值减1,即countArray[countIndex]–

二、maven依赖

pom.xml

<dependencies>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter</artifactId>
        <version>2.6.0</version>
    </dependency>

    <dependency>
        <groupId>org.projectlombok</groupId>
        <artifactId>lombok</artifactId>
        <version>1.16.14</version>
    </dependency>
</dependencies>

三、流程解析

  假设我们要排序的数据是:8, 10, 12, 9, 8, 12, 8

3.1、计数流程图

  首先我们看看计数统计是什么一回事,计数统计就是把数组中每个元素出现的次数都记录下来,并且能够通过元素找到对应的次数。
在这里插入图片描述

3.2、计数数组变形

  那么计数数组变形又是干啥呢?计数数组变形是从第一项开始,每一项都等于它本身和前一项的和,这样做,得到的值的意思是当前值前面还有多个数字,比如arr[1]=4,表示当前值前面有4-1=3个数字;arr[2]=5,表示当前值前面有5-1=4个数字。
在这里插入图片描述

3.3、排序过程

  最后我们看下具体是怎么排序的,又我们的数组的值推导得到索引,然后从计数数组中找到应该要排的位置,最后插入到对应的数组中,这种方式也是一种稳定的排序方式。
在这里插入图片描述

四、编码实现

  具体的编码实现如下,下面的实现方式也是稳定排序的方式。

/**
 * 计数排序
 *
 * @param arr
 * @return
 */
public static void countingSort(int[] arr) {
    if (arr.length == 0) {
        return;
    }
    // 原数组拷贝一份
    int[] copyArray = Arrays.copyOf(arr, arr.length);
    // 初始化最大最小值
    int max = Integer.MIN_VALUE;
    int min = Integer.MAX_VALUE;
    // 找出最小值和最大值
    for (int num : copyArray) {
        max = Math.max(max, num);
        min = Math.min(min, num);
    }

    // 新开辟一个数组用于统计每个元素的个数(范围是:最大数-最小数+1)
    int[] countArray = new int[max - min + 1];
    // 增强for循环遍历
    for (int num : copyArray) {
        // 加上最小偏差是为了让最小值索引从0开始,同时可有节省空间,每出现一次数据就加1
        // 真实值+偏差=索引值
        countArray[num - min]++;
    }
    log.info("countArray的初始值:{}", countArray);

    // 获取数组的长度
    int length = countArray.length;
    // 计数数组变形,新元素的值是前面元素累加之和的值
    for (int i = 1; i < length; i++) {
        countArray[i] = countArray[i] + countArray[i - 1];
    }
    log.info("countArray变形后的值:{}", countArray);

    // 遍历拷贝数组中的元素,填充到原数组中去,从后往前遍历
    for (int j = copyArray.length - 1; j >= 0; j--) {
        // 数据对应计数数组的索引
        int countIndex = copyArray[j] - min;
        // 数组的索引获取(获取到的计数数组的值n就是表示当前数据前有n-1个数据,数组从0开始,故当前元素的索引就是n-1)
        int index = countArray[countIndex] - 1;
        // 数组中的值直接赋值给原数组
        arr[index] = copyArray[j];
        // 计数数组中,对应的统计值减1
        countArray[countIndex]--;
        log.info("countArray操作后的值:{}", countArray);
    }
    log.info("排列结果的值:{}", arr);

}

public static void main(String[] args) {
    int[] arr = new int[]{8, 10, 12, 9, 8, 12, 8};
    log.info("要排序的初始化数据:{}", arr);
    //从小到大排序
    countingSort(arr);
    log.info("最后排序后的结果:{}", arr);
}

运行结果:

要排序的初始化数据:[8, 10, 12, 9, 8, 12, 8]
countArray的初始值:[3, 1, 1, 0, 2]
countArray变形后的值:[3, 4, 5, 5, 7]
countArray操作后的值:[2, 4, 5, 5, 7]
countArray操作后的值:[2, 4, 5, 5, 6]
countArray操作后的值:[1, 4, 5, 5, 6]
countArray操作后的值:[1, 3, 5, 5, 6]
countArray操作后的值:[1, 3, 5, 5, 5]
countArray操作后的值:[1, 3, 4, 5, 5]
countArray操作后的值:[0, 3, 4, 5, 5]
排列结果的值:[8, 8, 8, 9, 10, 12, 12]
最后排序后的结果:[8, 8, 8, 9, 10, 12, 12]
  • 6
    点赞
  • 15
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值