比快排还要快的排序——计数排序

news/2024/5/19 23:07:34 标签: 算法, 数据结构, java, 排序算法, 快速排序

有这样一道排序题:数组里有20个随机数,取值范围为从0到10,要求用最快的速度把这20个整数从小到大进行排序。

这道题就可以用计数排序来解,这种排序算法不是基于元素比较,而是利用数组下标来确定元素的正确位置

在刚才的题目里,随即整数的取值范围是从0到10,那么这些整数的值肯定是在0到10这11个数里面。于是我们可以建立一个长度为11的数组,数组下标从0到10,元素初始值全为 0,如下所示:

00000000000
index012345678910

加入现在给出一个20个随机数,取值范围为从0到10

9, 3, 5, 4, 9, 1, 2, 7, 8,1,3, 6, 5, 3, 4, 0, 10, 9, 7, 9

遍历数组,依次将数字出现的次数的次数填入上面的长度为11的数组,最终结果如下:

12232212141
index012345678910

有了这个统计结果,排序就很简单了,直接遍历数组,输出数组元素的下标值,元素的值是几,就输出几次:

0, 1, 1, 2, 3, 3, 3, 4, 4, 5, 5, 6, 7, 7, 8, 9, 9, 9, 9, 10

这就是计数排序的基本过程,它适用于一定范围的整数排序。在取值范围不是很大的情况下,它的性能在某些情况甚至快过那些O(nlogn)的排序,例如快速排序、归并排序。

说白了,还是用空间换时间。

给出一个数组arr,其取值范围为0-n

设计一个函数对它进行排序

Java实现如下

java">public class Solution6 {
    public static void main(String[] args) {
      	//测试用例
        int[] a = {1,2,1,3,2,4,5,6,7};
        int[] b = {1};
        int[] c = {0,0,0,0};
        int[] d = {1,1,1,1};
     	int[] e = {0,2,1,3,2,4,0,6,7};
        int[] res = countSort(d,1);
        for(int each:res) System.out.println(each);
    }

    public static int[] countSort(int[] arr, int n){
        int len = arr.length;
        if(null == arr || arr.length == 0) return null;

        int[] nArray = new int[n+1];
        Arrays.fill(nArray,-1);
        for(int i=0; i<len; i++){
            if(nArray[arr[i]] == -1) nArray[arr[i]] = 1;
            else nArray[arr[i]]++;
        }
        
        int index = 0;
        for(int i=0; i<n+1; i++){
            if(nArray[i] == -1) continue;
            for(int j=0; j<nArray[i]; j++){
                arr[index] = i;
                index++;
            }
        }
        return arr;
    }
}

虽然是两个for循环嵌套,但是遍历的次数只是遍历arr的长度

时间复杂度:O(n),跟arr的长度有关

空间复杂度:O(n),跟取值范围n有关


http://www.niftyadmin.cn/n/1690301.html

相关文章

八大排序系列之「归并排序」

关于归并排序的原理这里不再赘述&#xff0c;直接附上代码 Java实现如下&#xff1a; public class Solution6 {public static void mergeSort(int[] arr){int[] tempArr new int[arr.length];sort(arr,tempArr,0,arr.length-1);}private static void sort(int[] arr,int[] …

Java多线程之「线程的生命周期」

在 Java 当中&#xff0c;线程通常都有五种状态&#xff0c;创建&#xff08;new&#xff09;、就绪&#xff08;runnable&#xff09;、运行&#xff08;running&#xff09;、阻塞&#xff08;blocked&#xff09;和死亡&#xff08;dead&#xff09;。 新建(NEW)&#xff1a…

归并排序衍生之「单向链表归并排序」

这道题算是一道比较综合的题&#xff1a; 快慢指针寻找中间节点合并两个有序的链表归并排序的思想 思想&#xff1a; 设置两个指针&#xff0c;一个步长为1&#xff0c; 一个步长为2&#xff0c;当快指针到达尾结点时&#xff0c;慢指针指向中间结点&#xff0c;时间复杂度为O…

剑指Offer系列之「两个链表的第一个公共节点」

两个链表的第一个公共节点 Java 实现如下&#xff1a; /* public class ListNode {int val;ListNode next null;ListNode(int val) {this.val val;} }*/ public class Solution {public ListNode FindFirstCommonNode(ListNode pHead1, ListNode pHead2) {if(pHead1 null …

剑指Offer系列之「跳台阶」

一只青蛙一次可以跳上1级台阶&#xff0c;也可以跳上2级。求该青蛙跳上一个n级的台阶总共有多少种跳法&#xff08;先后次序不同算不同的结果&#xff09;。 递归实现&#xff1a; public class Solution {public int JumpFloor(int target) {if(target < 1) return 1;retu…

剑指Offer系列之「变态跳台阶」

一只青蛙一次可以跳上1级台阶&#xff0c;也可以跳上2级……它也可以跳上n级。求该青蛙跳上一个n级的台阶总共有多少种跳法。 状态转移方程&#xff1a; f(n) f(n-1) f(n-2) ... f(1) f(0) f(n-1) f(n-2) ... f(1) f(0) 合并一下&#xff1a;f(n) 2*f(n-1) 直接迭…

剑指Offer系列之「矩阵覆盖」

矩阵覆盖 我们可以用 2*1 的小矩形横着或者竖着去覆盖更大的矩形。请问用 n 个 2*1 的小矩形无重叠地覆盖一个2n的大矩形&#xff0c;总共有多少种方法&#xff1f; 比如 n3 时&#xff0c;23 的矩形块有 3 种覆盖方法&#xff1a; Java 实现如下&#xff1a; public class So…

剑指Offer系列之「连续子数组的最大和」

连续子数组的最大和 在古老的一维模式识别中&#xff0c;常常需要计算连续子向量的最大和&#xff0c;当向量全为正数的时候&#xff0c;问题很好解决。但是&#xff0c;如果向量中包含负数&#xff0c;是否应该包含某个负数&#xff0c;并期望旁边的正数会弥补它呢&#xff1f…