排序算法-计数排序:算法难理解?执行过程打印出来一步一步看

计数排序(Counting Sort)是一种非比较排序算法,它的基本思想是利用数组下标的有序性,将元素作为计数数组的相应下标,并在该下标位存储元素数量,最后根据计数数组填充待排序数组。

计数排序的具体步骤如下:

1、找出待排序数组中的最大值和最小值。

2、创建一个大小为 (最大值 - 最小值 + 1) 的计数数组,数组中每个元素表示待排序数组中值为 (最小值 + i) 的元素个数。

3、遍历待排序数组,统计每个值出现的次数,并将统计结果保存在计数数组中。

4、对计数数组进行前缀和操作,得到每个值在排序后数组中的下标位置。

5、遍历待排序数组,根据计数数组中的统计结果将每个元素放置到正确的位置上。

执行过程请移步文章末尾:执行过程。

计数排序有以下几个优点:

1、时间复杂度为 O(n + k),其中 n 是待排序数据的个数,k 是待排序数据的范围,因此在数据范围不大的情况下,计数排序的时间复杂度是非常优秀的。

2、计数排序是一种稳定排序算法,相同大小的元素在排序后仍然保持着它们之间的相对顺序。

3、由于计数排序不需要进行比较操作,因此它的时间复杂度与待排序数据本身的分布情况无关。

但是,计数排序也存在一些缺点:

1、计数排序只适用于数据范围比较小的情况,如果数据范围过大,计数数组会占用过多的内存空间。

2、计数排序只能用于非负整数的排序,如果待排序数据包含负数,需要先将数据整体加上一个固定值,再进行计数排序。

代码实现:

package com.algorithm.sort;

import java.util.Arrays;

/**
 * @author LuoFei
 * @className: CountingSort
 * @projectName algorithm
 * @description: TODO
 * @date 2023/2/27 14:10
 */
public class CountingSort {

    public static void countingSort(int[] arr) {
        int len = arr.length;
        if (len <= 1) {
            System.out.println("数组长度小于2位,无需排序,直接返回");
            return;
        }

        //确定数据范围
        System.out.println("找到数组中的最大值与最小值");
        int max = arr[0], min = arr[0];
        for (int i = 0; i < len; i++) {
            if (arr[i] > max) {
                max = arr[i];
            }
            if (arr[i] < min) {
                min = arr[i];
            }
        }
        System.out.println("最大值:"+max+", 最小值:"+min);

        int range = max - min + 1;
        //创建计数数组
        int[] count = new int[range];
        System.out.println("创建用于计数的数组,数组大小:"+(range));
        System.out.println("遍历待排序数组,将待排序数组元素值减去最小值,结果作为计数数组下标,该下标计数数组值加1。");
        for (int i = 0; i < arr.length; i++) {
            System.out.println("arr["+i+"]="+arr[i]);
            count[arr[i] - min]++;
            System.out.println("计数数组下标: "+(arr[i]-min)+"的值加1,count["+(arr[i]-min)+"]="+count[arr[i] - min]);
        }
        System.out.println("计数数组:"+Arrays.toString(count));
        System.out.println("----------------------------------------");

        System.out.println("对计数数组进行前缀和操作, 得出计数数组下标在待排序数组中位置");
        for (int i = 1; i < count.length; i++) { // 对计数数组进行前缀和操作
            count[i] += count[i - 1];
        }
        System.out.println("新计数数组:"+Arrays.toString(count));

        System.out.println("创建临时数组用于存放排序后数组");
        int[] temp = new int[len];
        System.out.println("对待排序数组倒序遍历");
        for (int i = len - 1; i >= 0; i--) { // 将待排序数组中的元素放置到正确的位置上
            System.out.println("从计数数组第一位开始查找元素arr["+i+"]: "+arr[i]+", 计数数组下标为:"+(arr[i] - min)+",在排序后数组中位置:"+(count[arr[i] - min]-1));
            temp[count[arr[i] - min] - 1] = arr[i];
            count[arr[i] - min]--;
            System.out.println("计数数组count["+(arr[i]-min)+"]值减1");
            System.out.println("新计数数组:"+Arrays.toString(count));
            System.out.println("临时数组:"+Arrays.toString(temp));
        }

        System.out.println("将临时数组赋值原数组");
        for (int i = 0; i < len; i++) { // 将排序后的数组赋值给原数组
            arr[i] = temp[i];
        }
    }
    public static void main(String[] args) {
        int[] arr = {5, 2, 8, 3, 9, 1};
        System.out.println("计数排序");
        System.out.println("初始数组:"+ Arrays.toString(arr));
        System.out.println("==============开始计数排序===============");
        countingSort(arr);
        System.out.println("==============计数排序完成===============");
        System.out.println("最终数组"+Arrays.toString(arr));
    }
}

执行过程:

计数排序
初始数组:[5, 2, 8, 3, 9, 1]
==============开始计数排序===============
找到数组中的最大值与最小值
最大值:9, 最小值:1
创建用于计数的数组,数组大小:9
遍历待排序数组,将待排序数组元素值减去最小值,结果作为计数数组下标,该下标计数数组值加1。
arr[0]=5
计数数组下标: 4的值加1,count[4]=1
arr[1]=2
计数数组下标: 1的值加1,count[1]=1
arr[2]=8
计数数组下标: 7的值加1,count[7]=1
arr[3]=3
计数数组下标: 2的值加1,count[2]=1
arr[4]=9
计数数组下标: 8的值加1,count[8]=1
arr[5]=1
计数数组下标: 0的值加1,count[0]=1
计数数组:[1, 1, 1, 0, 1, 0, 0, 1, 1]
----------------------------------------
对计数数组进行前缀和操作, 得出计数数组下标在待排序数组中位置
新计数数组:[1, 2, 3, 3, 4, 4, 4, 5, 6]
创建临时数组用于存放排序后数组
对待排序数组倒序遍历
从计数数组第一位开始查找元素arr[5]: 1, 计数数组下标为:0,在排序后数组中位置:0
计数数组count[0]值减1
新计数数组:[0, 2, 3, 3, 4, 4, 4, 5, 6]
临时数组:[1, 0, 0, 0, 0, 0]
从计数数组第一位开始查找元素arr[4]: 9, 计数数组下标为:8,在排序后数组中位置:5
计数数组count[8]值减1
新计数数组:[0, 2, 3, 3, 4, 4, 4, 5, 5]
临时数组:[1, 0, 0, 0, 0, 9]
从计数数组第一位开始查找元素arr[3]: 3, 计数数组下标为:2,在排序后数组中位置:2
计数数组count[2]值减1
新计数数组:[0, 2, 2, 3, 4, 4, 4, 5, 5]
临时数组:[1, 0, 3, 0, 0, 9]
从计数数组第一位开始查找元素arr[2]: 8, 计数数组下标为:7,在排序后数组中位置:4
计数数组count[7]值减1
新计数数组:[0, 2, 2, 3, 4, 4, 4, 4, 5]
临时数组:[1, 0, 3, 0, 8, 9]
从计数数组第一位开始查找元素arr[1]: 2, 计数数组下标为:1,在排序后数组中位置:1
计数数组count[1]值减1
新计数数组:[0, 1, 2, 3, 4, 4, 4, 4, 5]
临时数组:[1, 2, 3, 0, 8, 9]
从计数数组第一位开始查找元素arr[0]: 5, 计数数组下标为:4,在排序后数组中位置:3
计数数组count[4]值减1
新计数数组:[0, 1, 2, 3, 3, 4, 4, 4, 5]
临时数组:[1, 2, 3, 5, 8, 9]
将临时数组赋值原数组
==============计数排序完成===============
最终数组[1, 2, 3, 5, 8, 9]
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值