排序算法之计数排序

目录

1、什么是计数排序

2、计数排序的应用场景

3、计数排序的思想

4、计数排序的步骤

4.1、计数排序的朴素方法步骤:

4.2、该方法存在的问题:

4.3、解决方法:

4.4、计数排序的最终步骤

5、代码实现

6、总结

7、计数排序的类比


1、什么是计数排序

计数排序是一种非比较排序,其核心是将序列中的元素作为键存储在额外的数组空间中,而该元素的个数作为值存储在数组空间中,通过遍历该数组排序。

2、计数排序的应用场景

  • 序列中最大值和最小值之间的差值不能过大,这主要是防止建立数组时造成内存的浪费。
  • 序列中存在的元素是整数,因为我们使用的是该元素作为键存储在额外的数组空间中,如果不是整数,不能作为键。

3、计数排序的思想

         计数排序的核心是:利用数组的索引是有序的,通过将序列中的元素作为索引,其个数作为值放入数组,遍历数组来排序。

4、计数排序的步骤

4.1、计数排序的朴素方法步骤:

  • 从无序数组中取出最大值max,新建一个长度为max+1的数组。
  • 遍历无序数组,取其中元素作为新建数组的索引,存在一个则新数组该索引所在的值自增。
  • 遍历新数组,当存在不为0的元素,取该元素的索引放入最终数组,并且该元素自减,直到为0,返回最终数组

4.2、该方法存在的问题:

  • 新建一个长度为max+1的数组会造成内存的浪费,比如元素为{400,405,410}则新建数组的长度为411,这会使前面的0 ~ 400索引没用,造成内存浪费。
  • 其将元素作为键,将个数作为值放入新的数组中,但是如果存在相同的元素,我们只统计其个数,其顺序无法确定,是不稳定的。

4.3、解决方法:

  • 问题1的解决方法是:取数组中的最大值max和最小值min,新建(max-min +1)长度的数组,数组的元素存放在新数组中的(arr[i]-min)索引处。
  • 问题2的解决方法是:新建一个统计数组,其长度为(max-min +1),其索引存放的是新数组该索引之前元素的和,这个和表示的是该索引(该元素)在原数组中的排序顺序,就是排第几。

4.4、计数排序的最终步骤

  • 取无序数组arr中的最大值max和最小值min,新建(max-min +1)长度的数组newArr和长度为(max-min +1)的统计数组countArr。
  • 遍历原数组arr,将其值作为newArr的键,元素的个数作为值存放在该键处。
  • 遍历newArr,使统计数组countArr和newArr相同索引处存放的是newArr该索引之前元素的和。
  • 新建一个最终数组result,反向遍历原数组,取原数组的值arr[i]-min作为索引,从统计数组countArr取出该索引的值减1,作为最终数组result的索引,值为原数组的arr[i],同时统计数组该索引处值减1,遍历结束后,最终数组result为排序后的数组。

5、代码实现

package com.kgf.algorithm.sort;

/***
 * 计数排序
 */
public class CountSort {

    public static void main(String[] args) {
        int[] nums = {1, 4, 9, 2, 5, 3, 7, 6, 22, 23, 15, 24, 0, 3,
                4, 5, 2, 3, 5, 12, 1, 3, 4, 2, 1,
                3, 45, 1, 1};
        // 计数排序
        CountSort countSort = new CountSort();
//        countSort.sort(nums);
        int[] ints = countSort.sort2(nums);
        for (int num : ints) {
            System.out.print(num+"\t");
        }
        System.out.println();
    }

    /***
     * 计数排序优化
     * @param nums
     */
    public int[] sort2(int[] nums){
        //找到数组最大值和最小值
        int max = nums[0],min = nums[0];
        for (int i = 1; i < nums.length; i++) {
            if (max<nums[i]){
                max = nums[i];
            }
            if (min>nums[i]){
                min = nums[i];
            }
        }

        //首先创建一个固定长度的空数组
        int[] newArr = new int[max-min+1];
        //将nums的值作为下标,数量作为值存入newarr
        for (int i = 0; i < nums.length; i++) {
            newArr[nums[i]-min]++;
        }

        //为了防止相同的元素顺序不一致,使用countArr数组进行排序
        int[] countArr = new int[newArr.length];
        for (int i = 0; i < newArr.length; i++) {
            if (i==0){
                countArr[i] = newArr[i];
                continue;
            }
            countArr[i] = newArr[i]+countArr[i-1];
        }
        //定义一个数组用来存储排序后的元素
        int[] result = new int[nums.length];
        for (int i = 0; i < nums.length; i++) {
            result[countArr[nums[i]-min]-1] = nums[i];
            countArr[nums[i]-min]--;
        }
        return result;
    }

    /***
     * 简单的计数方法
     * @param nums
     * @return
     */
    public void sort(int[] nums){
        //找到数组最大值
        int max = nums[0];
        for (int i = 1; i < nums.length; i++) {
            if (max<nums[i]){
                max = nums[i];
            }
        }
        //首先创建一个固定长度的空数组
        int[] newArr = new int[max+1];
        //将nums的值作为下标,数量作为值存入newarr
        for (int i = 0; i < nums.length; i++) {
            newArr[nums[i]]++;
        }
        //创建一个最终的空数组存储
        int j = 0;
        for (int i = 0; i < newArr.length; i++) {
            for (int k = 0; k < newArr[i]; k++) {
                nums[j] = i;
                j++;
            }
        }
    }
}

6、总结

        计数排序是非比较排序,时间复杂度是O(n+k),空间复杂度是O(k),是稳定算法。(n表示的是数组的个数,k表示的max-min+1的大小)

时间复杂度是O(n+k):通过上面的代码可知最终的计数算法花费的时间是3n+k,则时间复杂度是O(n+k)。

空间复杂度是O(k):如果出去最后的返回数组,则空间复杂度是2k,则空间复杂度是O(k)

稳定算法:由于统计数组可以知道该索引在原数组中排第几位,相同的元素其在原数组中排列在后面,其从原数组的后面遍历,其在最终数组中的索引也在后面,所以相同的元素其相对位置不会改变。

7、计数排序的类比

 计数排序的重点是将序列中的元素作为键存储在额外的数组空间中,通过遍历来排序。类似于:我要给一系类弹珠从小到大排列,我可以拿一个模具,模具上有从小到大的孔洞,我把弹珠放进孔洞中,最后只要从模具中从小到大的孔洞中倒出弹珠即可。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
基数排序(Radix Sort)是一种非比较排序算法,它根据元素的大小,将元素分配到不同的桶中进行排序。计数排序(Counting Sort)是一种稳定的排序算法,它利用桶的思想,对每个元素出现的次数进行统计,再根据桶的顺序依次输出排序结果。 基数排序可以使用计数排序作为其内部排序算法,具体实现步骤如下: 1. 找到最大数,并确定其位数 2. 对所有数按照个位数进行排序,利用计数排序 3. 对所有数按照十位数进行排序,利用计数排序 4. 重复步骤 3,直到所有位数都排完序 下面是使用 C++ 实现基数排序的代码: ```cpp #include <iostream> #include <vector> using namespace std; void countingSort(vector<int>& arr, int exp) { vector<int> count(10, 0); vector<int> output(arr.size()); // 统计每个数位上出现的数字的个数 for (int i = 0; i < arr.size(); i++) { int digit = (arr[i] / exp) % 10; count[digit]++; } // 计算每个数字在输出数组中的位置 for (int i = 1; i < count.size(); i++) { count[i] += count[i - 1]; } // 将元素从输入数组复制到输出数组中,保证稳定性 for (int i = arr.size() - 1; i >= 0; i--) { int digit = (arr[i] / exp) % 10; output[count[digit] - 1] = arr[i]; count[digit]--; } // 将排序好的数组赋值给原数组 for (int i = 0; i < arr.size(); i++) { arr[i] = output[i]; } } void radixSort(vector<int>& arr) { int max_num = *max_element(arr.begin(), arr.end()); // 从个位开始,依次对每个数位进行排序 for (int exp = 1; max_num / exp > 0; exp *= 10) { countingSort(arr, exp); } } int main() { vector<int> arr = { 170, 45, 75, 90, 802, 24, 2, 66 }; radixSort(arr); for (auto num : arr) { cout << num << " "; } return 0; } ``` 输出结果为:2 24 45 66 75 90 170 802

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值