重温计数排序 ╰+哭是因爲堅強的太久メ 2023-06-30 01:52 70阅读 0赞 一开始想,对数组进行排序,肯定是要比较的呀,不比较怎么知道大小。直到遇见了计数排序,桶排序,基数排序。真是感叹人类的牛逼。居然还能这样? 话不多说,下面开始重温计数排序 * 基本思路:先从简单的例子说起。如果给定一个大小为10的数组,并且知道数组里的元素都在0-10的范围内。那么可以建一个长度为11的数组(初始值为0)用于计数。再遍历原始数组,以数组元素的值,作为下标,定位到计数数组中的某个位置,对其值进行加一。也就是把原数组中各个元素出现的次数给统计了一遍。假设原数组为\[2,3,5,9,5,4,1,7,3,2\]。那么得到的计数数组为\[0,1,2,2,1,5,0,1,0,1,0\]。再遍历计数数组,某个下标的元素值为多少,就将下标输出多少次。于是得到\[1,2,2,3,3,4,5,7,9\] * 局限性: * 当数组最大值和最小值相差过大时,不适用(计数数组的空间有很大一部分用不上,很浪费) * 当数组元素不是整数时,不适用(计数数组是用下标来代表原数组的值,下标位置的元素值,代表该下标的数字,出现的次数) * 优化: 朴素版的计数排序,只是对各个元素出现的次数做了简单统计,然后从小到大按出现次数将元素打印了一遍,不能区分相同元素的先后顺序。可以在统计完元素出现次数后,对次数数组进行变形,让次数数组的每个元素,都加上之前的所有元素之和。 比如有4个学生 * 小红 95分 * 小绿 93分 * 小白 95分 * 大黄 99分 最大值和最小值的差为 99 - 93 = 6,那么我们创建一个大小为7的数组用于计数,下标0代表93分。于是得到计数数组为\[1,0,2,0,0,0,1\],之后对计数数组进行变形,每一项都加上前面所有项的和,得到变形后的计数数组 \[1,1,3,3,3,3,4\]。创建一个新的数组用于储存排序结果(长度和原数组一致),然后我们对原数组,从后往前遍历,先取大黄,99分,99-93=6,找到计数数组下标为6的元素,取出,得4,代表大黄排名第4(按从小到大往后排)。于是找到新数组中第四个元素的位置(下标为3),将大黄设置进去,新数组为\[null,null,null,大黄\],此时将计数数组对应的值-1 ,计数数组为\[1,1,3,3,3,3,3\],最后一个位置的值变成了3,表示如果再出现99分的同学,即排在第三位。继续遍历原数组,到小白,95分,95-93=2,取计数数组中下标为2的元素,得3,表明95分排第三位,于是对新数组的第三个元素进行设置,得\[null,null,小白,大黄\],对计数数组对应值减1,得\[1,1,2,3,3,3,3\]。再遍历到小绿,93分,93-93=0,取计数数组下标为0的元素,得1,设置新数组,得\[小绿,null,小白,大黄\],对计数数组进行减1,得\[0,1,2,3,3,3,3\]。遍历原数组,到小红,95分,95-93=2,取计数数组,得2,设置新数组的第二个位置(下标1),得\[小绿,小红,小白,大黄\]。可见最终的有序数组中,小红和小白的相对位置没有发生变化,是稳定的排序。 这种通过对计数数组进行变形的方式,保留了某个元素在整个数组中的位置的信息(比某个元素小的,有多少个元素),并通过对原数组从后往前遍历,并逐步对计数数组进行减一操作,能够保留相同元素的原先的相对位置。 计数排序的时间复杂度是O(n + m),空间复杂度是O(m),(n是原数组大小,m是最大值和最小值之差)。最后一步,贴代码,告辞,桶排和鸡排再见! package sort.core; import lombok.AllArgsConstructor; import lombok.Getter; import lombok.Setter; import org.junit.Test; import utils.ArrayUtil; import java.util.Arrays; import java.util.function.IntFunction; import static utils.ArrayUtil.printArray; /** Not based on compare **/ /*** * 适用于一定范围内的整数排序,在取值范围不大的情况下,性能比较好 * 局限: * 不适用于小数 * 最大值与最小值差距过大时,也不适用 */ public class CountSort { public void countSort(int[] arr){ if (arr == null || arr.length <= 1) return ; int min,max; min = max = arr[0]; for (int i = 1; i < arr.length; i++){ if (arr[i] < min) min = arr[i]; if (arr[i] > max) max = arr[i]; } int[] count = new int[max - min + 1]; for (int i = 0; i < arr.length; i++){ count[arr[i] - min]++; } int k = 0; for (int i = 0; i < max - min + 1; i++){ int cnt = count[i]; while (cnt > 0){ arr[k++] = i + min; cnt--; } } } /** * 优化,相同元素可以区分 * 从第二个元素开始,每个元素要加上前面所有元素的和 * **/ @Setter @Getter @AllArgsConstructor static class Student{ private String name; private int score; @Override public String toString() { return "Student{" + "name='" + name + '\'' + ", score=" + score + '}'+'\n'; } } /** 可以保证稳定 **/ /** 时间复杂度为 3n + m , 为 O(n + m) **/ /** n为原始数组长度,m为 max - min + 1 **/ /** 空间复杂度(不考虑结果数组):为 O(m) **/ public Student[] countSortEnhanced(Student[] arr){ if (arr == null || arr.length <= 1) return arr; int min,max; min = max = arr[0].getScore(); for (int i = 1; i < arr.length ; i++){ if (arr[i].getScore() < min) min = arr[i].getScore(); if (arr[i].getScore() > max) max = arr[i].getScore(); } /** 计数 **/ int[] scoreCnt = new int[max - min + 1]; for (int i = 0; i < arr.length; i++){ scoreCnt[arr[i].getScore() - min]++; } /** 变形 **/ for (int i = 0; i < max - min + 1; i++){ if (i == 0) continue; scoreCnt[i] += scoreCnt[i - 1]; } /**对原数组从后往前遍历**/ Student[] sortedArr = new Student[arr.length]; for (int i = arr.length - 1; i >= 0; i--){ /** 注意排名第一,要插入到第0号位置,所以用前置--,而不是后置-- **/ int pos = --scoreCnt[arr[i].getScore() - min]; sortedArr[pos] = arr[i]; } return sortedArr; } @Test public void test2(){ Student[] arr = Arrays.asList( new Student("yogurt",95), new Student("amber",94), new Student("lily",96), new Student("potter",99), new Student("harry",94), new Student("tom",99) ).stream().toArray(Student[]::new); Student[] sorted = countSortEnhanced(arr); printArray(sorted); } @Test public void test(){ int[] arr = { 2,-8,5,12,1,3,4,9,10,5,7,6}; countSort(arr); printArray(arr); } }
相关 计数排序 数排序是非比较排序,桶排序的一种 时间复杂度:O(n),空间复杂度O(n) ![watermark_type_ZmFuZ3poZW5naGVpdGk_shadow_1... 一时失言乱红尘/ 2024年04月18日 08:20/ 0 赞/ 150 阅读
相关 重温计数排序 一开始想,对数组进行排序,肯定是要比较的呀,不比较怎么知道大小。直到遇见了计数排序,桶排序,基数排序。真是感叹人类的牛逼。居然还能这样? 话不多说,下面开始重温计数排序 ╰+哭是因爲堅強的太久メ/ 2023年06月30日 01:52/ 0 赞/ 71 阅读
相关 计数排序 计数排序 冒泡、选择、插入、归并、快速、希尔、堆排序,都是基于比较的排序,平均时间复杂度最低是O(nlogn)。 计数排序、桶排序、基数排序,都不是基于比较的排序,它们 痛定思痛。/ 2023年02月20日 10:43/ 0 赞/ 57 阅读
相关 【排序】计数排序 为什么记录这个 在做 [leetcode 561 题][leetcode 561]的时候,第三种解法,看了很久才明白,后来理解实际上是做了一次计数排序。 这个题,直接看 缺乏、安全感/ 2022年11月20日 02:00/ 0 赞/ 298 阅读
相关 计数排序 转载自:[http://www.cnblogs.com/eaglet/archive/2010/09/16/1828016.html][http_www.cnblogs.com Bertha 。/ 2022年08月11日 08:00/ 0 赞/ 283 阅读
相关 计数排序 计数排序,他的主要目的是对整数排序并且会比普通的排序算法性能更好。 1. 初始化一个计数数组,大小是输入数组中的最大的数。 2. 遍历输入数组,遇到一个数 布满荆棘的人生/ 2022年07月14日 01:26/ 0 赞/ 315 阅读
相关 计数排序 对于一个int数组,请编写一个计数排序算法,对数组元素排序。 给定一个int数组A及数组的大小n,请返回排序后的数组。 测试样例: [1,2,3,5,2, 系统管理员/ 2022年06月09日 01:36/ 0 赞/ 308 阅读
相关 计数排序 计数排序 算法描述:是一种通过计数来达到排序的方法。 ![20180326152550965][] 1.选出数组的最大值k,创建一个k+1长度的数组coun 素颜马尾好姑娘i/ 2022年05月28日 00:20/ 0 赞/ 326 阅读
相关 计数排序 / 计数排序:统计小于等于该元素值的元素的个数i,于是该元素就放在目标数组的索引i位(i≥0)。 计数排序基于一个假设,待排序数列的 ゝ一纸荒年。/ 2021年12月18日 05:07/ 0 赞/ 398 阅读
相关 计数排序 计数排序: 假设n个输入元素中的每一个都是在0~区间内的一个整数,其中k为某个整数。当k=O(n)是,排序时间为O(n). 基本思想:对每个输入元素x,确定小于x元素的 淡淡的烟草味﹌/ 2021年09月23日 03:22/ 0 赞/ 516 阅读
还没有评论,来说两句吧...