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

目录

    • 一、前言
      • 1.1、概念
      • 1.2、算法步骤
    • 二、maven依赖
    • 三、流程解析
      • 3.1、计数流程图
      • 3.2、计数数组变形
      • 3.3、排序过程
    • 四、编码实现

一、前言

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.bootgroupId>
        <artifactId>spring-boot-starterartifactId>
        <version>2.6.0version>
    dependency>

    <dependency>
        <groupId>org.projectlombokgroupId>
        <artifactId>lombokartifactId>
        <version>1.16.14version>
    dependency>
dependencies>

三、流程解析

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

3.1、计数流程图

  首先我们看看计数统计是什么一回事,计数统计就是把数组中每个元素出现的次数都记录下来,并且能够通过元素找到对应的次数。
(十一)Java算法:计数排序(详细图解)_第1张图片

3.2、计数数组变形

  那么计数数组变形又是干啥呢?计数数组变形是从第一项开始,每一项都等于它本身和前一项的和,这样做,得到的值的意思是当前值前面还有多个数字,比如arr[1]=4,表示当前值前面有4-1=3个数字;arr[2]=5,表示当前值前面有5-1=4个数字。
(十一)Java算法:计数排序(详细图解)_第2张图片

3.3、排序过程

  最后我们看下具体是怎么排序的,又我们的数组的值推导得到索引,然后从计数数组中找到应该要排的位置,最后插入到对应的数组中,这种方式也是一种稳定的排序方式。
(十一)Java算法:计数排序(详细图解)_第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]

你可能感兴趣的:(Java常用算法,java,算法,排序算法,计数排序算法)