【算法】计数排序

317 阅读2分钟

携手创作,共同成长!这是我参与「掘金日新计划 · 8 月更文挑战」的第23天,点击查看活动详情

一、前言

计数排序 :是一种稳定的排序算法。它不是基于比较的排序算法,因此可以突破 O(n*log(n)) 的下界,在线性时间内完成排序。

  • 适用于:序列的键值是较小范围的整数,或是可以映射到较小范围整数的情况。

它的算法思想是:

  1. 统计相同键值的元素个数,然后以键值为下标,把统计结果存储到一个较小的数组中。
  2. 根据元素的出现个数,再计算出每个元素在排序数组中所在下标。
  3. 根据这些下标,把元素放到正确的位置上即可。

实现如下: 计数排序-2022-08-1301-48-45.png

public class CountingSortFixedK {

    private int k;

    // 对于数组中元素 x,有 0 <= x <= k, k 是一个较小的数字。
    public CountingSortFixedK(int k) {
        this.k = k;
    }

    // Time: O(n+k), Space: O(n+k)
    public void sortLeft2Right(int[] arr) {
        if (arr == null || arr.length == 0) return;
        // 1. 初始化索引数组:统计数字出现次数
        int[] indexes = new int[k + 1];
        for (int num : arr) ++indexes[num];

        // 2. 更新索引数组: 每个元素的开始下标
        int start = 0;
        for (int i = 0; i <= k; ++i) {
            int count = indexes[i];
            indexes[i] = start;
            start += count;
        }

        // 3. 遍历原数组,填充排序
        int[] tmp = new int[arr.length];
        for (int num : arr) {
            int idx = indexes[num];
            tmp[idx] = num;
            ++indexes[num];
        }
        // 拷贝回原数组
        System.arraycopy(tmp, 0, arr, 0, arr.length);
    }
}



二、题目

(1)排序数组(中)

LeetCode 912

题干分析

给你一个整数数组 nums,请你将该数组升序排列。

示例 1:
输入:nums = [5,2,3,1]
输出:[1,2,3,5]

示例 2:
输入:nums = [5,1,1,2,0,0]
输出:[0,0,1,1,2,5]

思路解法

计数排数,步骤如下:

  1. 找最大值和最小值: 因为数字中有负数,目的将负数对应的索引 “取正”
  2. 定义计数数组并统计
  3. 遍历输出结果
// 计数排序
// Time: O(n + k), Space: O(n + k), Faster: 99.97%
public int[] sortArrayCounting(int[] nums) {
    // 1. 找最大值和最小值
    int max = nums[0], min = nums[0];
    for (int num : nums) {
        max = Math.max(max, num);
        min = Math.min(min, num);
    }
    // 2. 定义计数数组并统计
    int[] counts = new int[max - min + 1];
    for (int num : nums) {
        ++counts[num - min]; // 落到数组上
    }
    // 3. 遍历输出结果
    int[] result = new int[nums.length];
    for (int i = 0, j = 0; i < max - min + 1; ++i) {
        while (counts[i]-- > 0) {
            result[j++] = i + min;
        }
    }
    return result;
}