计数排序是时间复杂度为 O(n)的算法,空间复杂度为O(n);算法思想跟散列表哈希hash有些类似,主要是利用一段有序数组计算对应元素的下表个数,然后依次输出有数组元素进行排列。基本计数排序是不稳定算法,但是优化后计数排序是稳定算法。本文主要讲解基本计数排序和优化后计数排序。

使用条件:数组必须是整数或者能全部映射为整数,数组所有元素必须在有限较集中范围;

一、具体实现步骤

1.计算原始数组的最大值max和最小值min范围,然后创建一个长度为max--min+1的排序数组sortArr(max--min+1);

该数组sortArr目的是计算每个原始数组每个元素的个数

auto minmax = std::minmax_element(nums.begin(), nums.end()); // minmax_element(begin,end)可以输入迭代器而minmax(l,r)不能输入迭代器
int min = *minmax.first;
int max = *minmax.second;
int index = 0;
vector<int> sortArr(max - min + 1, 0); // 创建一段数组用来存储每个元素个数
vector<int> result(nums.size()); // 创建一段数组用来存储每个元素个数

2.遍历原始数组,将原始数组的值作为排序数组sortArr的下标进行计数;

此时原始数组所有元素个数已经计算出来并顺序存放;注意:存储下标的时候需要减去最小值,将原始数组缩放到排序数组范围内。

for (const auto& it : nums) // 遍历原始数组,计算所有元素个数;sortArr下标是元素,值是该元素个数
{sortArr[it - min]++;
}

3.遍历排序数组sortArr,将所有非零的值取出下标作为输出数组元素;

这里注意取出下标的时候需要加上最小值,因为前面存入的时候减了最小值。

for (int i = 0; i < sortArr.size(); ++i) // 遍历有序数组,值为非零则取出排列
{while (sortArr[i]){result[index] = i + min;++index;--sortArr[i];}
}

4.最后返回结果数组或者赋值给原来数组

nums = result;

二、完整代码示例

Sorts.h

#pragma once#include <iostream>
#include <vector>using namespace std;struct Sorts {    void count(vector<int>& nums);    void print(vector<int>& nums);
};

Sorts.cpp

#include "Sorts.h"
#include <algorithm>void Sorts::count(vector<int>& nums)
{if (nums.size() <= 1)return;auto minmax = std::minmax_element(nums.begin(), nums.end()); // minmax_element(begin,end)可以输入迭代器而minmax(l,r)不能输入迭代器int min = *minmax.first;int max = *minmax.second;int index = 0;vector<int> sortArr(max - min + 1, 0); // 创建一段数组用来存储每个元素个数vector<int> result(nums.size()); // 创建一段数组用来存储每个元素个数for (const auto& it : nums) // 遍历原始数组,计算所有元素个数;sortArr下标是元素,值是该元素个数{sortArr[it - min]++;}for (int i = 0; i < sortArr.size(); ++i) // 遍历有序数组,值为非零则取出排列{while (sortArr[i]){result[index] = i + min;++index;--sortArr[i];}}nums = result;
}void Sorts::print(vector<int>& nums)
{for (const auto& it : nums)cout << it << ",";cout << endl;
}

main.cpp

 #include <vector>
#include "Sorts.h"using namespace std;int main()
{vector<int> nums = { 2,0,1,6,8,10,5,99,87,333,2,0,1 };Sorts sorts;sorts.print(nums);sorts.count(nums);sorts.print(nums);return 1;
}

输出结果:

三、优化版计数排序

正常版本的计数排序当有几个相同元素时,会改变原来元素相对位置,是不稳定排序,因此需要优化。

3.1 优化的思路

主要是将排序数组sortArr里非零元素作为权值依次加入到后面元素里。具体示例如下所示:

/*
  排序数组sortArr总共弄有334个元素
sortArr:  0  1  2  3  4  5  6  7  8  9  10  ...  87  ...  99   ...  333
count:    2  2  2  0  0  1  1  0  1  0  1   0    1   0    1    0    1

变化后排序数组sortArr(Di == Di-1 + Di-2 : 后面的元素等于前面非零元素之和)

第一次变化
sortArr:  0  1  2  3  4  5  6  7  8  9  10  ...  87  ...  99   ...  333
count:    2  4  2  0  0  1  1  0  1  0  1   0    1   0    1    0    1

第二次变化
sortArr:  0  1  2  3  4  5  6  7  8  9  10  ...  87  ...  99   ...  333
count:    2  4  6  0  0  1  1  0  1  0  1   0    1   0    1    0    1

第三次变化
sortArr:  0  1  2  3  4  5  6  7  8  9  10  ...  87  ...  99   ...  333
count:    2  4  6  6  0  1  1  0  1  0  1   0    1   0    1    0    1

第四次变化
sortArr:  0  1  2  3  4  5  6  7  8  9  10  ...  87  ...  99   ...  333
count:    2  4  6  6  6  1  1  0  1  0  1   0    1   0    1    0    1

第五次变化
sortArr:  0  1  2  3  4  5  6  7  8  9  10  ...  87  ...  99   ...  333
count:    2  4  6  6  0  7  1  0  1  0  1   0    1   0    1    0    1

第六次变化
sortArr:  0  1  2  3  4  5  6  7  8  9  10  ...  87  ...  99   ...  333
count:    2  4  6  6  0  1  8  0  1  0  1   0    1   0    1    0    1

第七次变化
sortArr:  0  1  2  3  4  5  6  7  8  9  10  ...  87  ...  99   ...  333
count:    2  4  6  6  0  1  1  8  1  0  1   0    1   0    1    0    1

...

第三百三十三次变化
sortArr:  0  1  2  3  4  5  6  7  8  9  10  ...   87  ...  99   ...  333
count:    2  4  6  6  6  7  8  8  9  9  10 10  11  11  12  12  13

*/

第333次变化后的排序数组sortArr的值即是输出有序数组的下标,索引即是缩放后的元素。代码实现如下:

for (auto& it : sortArr) // 遍历排序数组,将前面非零元素依次累加到后面
{if (it)tmp += it; // 非零则累加it = tmp; // 变化后元素
}
// 经过上面的变化,此时排序数组sortArr的元素即为初始数组元素排序后的位置

那么问题来了,该如何还原排序后元素呢?

答案很简单,因为变化后排序数组sortArr的值是原始数组nums元素排序后索引下标,所以只需要依次遍历原始数组nums,然后找打对应下标,将该原始数组的元素依次取出排列到有序数组即可,代码实现如下:

for (int i = nums.size(); i > 0; --i) // 逆序遍历原始数组,找到对应位置输出有序数组
{int index = sortArr[nums[i - 1]] - 1; // 找到元素nums[i-1]排序后位置索引;sortArr[ nums[i-1] ]的值表示元素nums[i-1]在有序数组中排第几个位置result[index] = nums[i - 1];sortArr[nums[i - 1]]--; // 排序数组取出一个元素排列后,该元素的值需要减一,即如果下次还命中该相同元素,则往后一位排列
}
nums = result;

3.2 优化计数排序跟基本计数排序主要区别

3.2.1变化后的排序数组sortArr存储的元素值不同

基本计数排序的排序数组sortArr存储的是初始化数组nums每个元素个数,而优化后计数排序sortArr的元素存储的是初始化数组nums排序后的位置(即排序后在第几位)

3.2.2遍历排序数组sortArr取元素方式不一样

基本计数排序主要是依次遍历排序数组sortArr,当其值非零时取元素并且该值减一,而优化计数排序主要是逆向遍历原始数组nums,通过在排序数组sortArr中找到原始数组nums每个元素的下标,而赋值给结果数组result

基本计数排序主要区别:

for (int i = 0; i < sortArr.size(); ++i) // 遍历有序数组,值为非零则取出排列
{while (sortArr[i]){result[index] = i + min;++index;--sortArr[i];}
}
nums = result;

优化计数排序主要区别:

for (auto& it : sortArr) // 遍历排序数组,将前面非零元素依次累加到后面
{if (it)tmp += it; // 非零则累加it = tmp; // 变化后元素
}
// 经过上面的变化,此时排序数组sortArr的元素即为初始数组元素排序后的位置for (int i = nums.size(); i > 0; --i) // 逆序遍历原始数组,找到对应位置输出有序数组
{int index = sortArr[nums[i - 1]] - 1; // 找到元素nums[i-1]排序后位置索引;sortArr[ nums[i-1] ]的值表示元素nums[i-1]在有序数组中排第几个位置result[index] = nums[i - 1];sortArr[nums[i - 1]]--; // 排序数组取出一个元素排列后,该元素的值需要减一,即如果下次还命中该相同元素,则往后一位排列
}
nums = result;

四、优化计数排序完整代码示例

Sorts.h

#pragma once#include <iostream>
#include <vector>using namespace std;struct Sorts {    void count1(vector<int>& nums);    void print(vector<int>& nums);
};

Sorts.cpp

#include "Sorts.h"
#include <algorithm>void Sorts::count1(vector<int>& nums)
{if (nums.size() <= 1)return;auto minmax = std::minmax_element(nums.begin(), nums.end()); // minmax_element(begin,end)可以输入迭代器而minmax(l,r)不能输入迭代器int min = *minmax.first;int max = *minmax.second;    int tmp = 0;vector<int> sortArr(max - min + 1, 0); // 创建一段数组用来存储每个元素个数vector<int> result(nums.size()); // 创建一段数组用来存储每个元素个数for (const auto& it : nums) // 遍历原始数组,计算所有元素个数;sortArr下标是元素,值是该元素个数{sortArr[it - min]++;}for (auto& it : sortArr) // 遍历排序数组,将前面非零元素依次累加到后面{if (it)tmp += it; // 非零则累加it = tmp; // 变化后元素}// 经过上面的变化,此时排序数组sortArr的元素即为初始数组元素排序后的位置for (int i = nums.size(); i > 0; --i) // 逆序遍历原始数组,找到对应位置输出有序数组{int index = sortArr[ nums[i-1] ] - 1; // 找到元素nums[i-1]排序后位置索引;sortArr[ nums[i-1] ]的值表示元素nums[i-1]在有序数组中排第几个位置result[index] = nums[i - 1];sortArr[nums[i - 1]]--; // 排序数组取出一个元素排列后,该元素的值需要减一,即如果下次还命中该相同元素,则往后一位排列}nums = result;
}void Sorts::print(vector<int>& nums)
{for (const auto& it : nums)cout << it << ",";cout << endl;
}

main.cpp

 #include <vector>
#include "Sorts.h"using namespace std;int main()
{vector<int> nums = { 2,0,1,6,8,10,5,99,87,333,2,0,1 };Sorts sorts;sorts.print(nums);sorts.count1(nums);sorts.print(nums);return 1;
}

输出结果:

计数排序算法——C++相关推荐

  1. JavaScript实现CountingSort计数排序算法(附完整源码)

    JavaScript实现CountingSort计数排序算法(附完整源码) Comparator.js完整源代码 Sort.js完整源代码 CountingSort.js完整源代码 Comparato ...

  2. 使其正序排序 打印一串数字_JavaScript计数排序算法

    一.计数排序算法 计数排序(Counting sort)是一种稳定的线性时间排序算法.该算法于1954年由 Harold H. Seward 提出.计数排序使用一个额外的数组,数组的下标对应待排序的数 ...

  3. 经典排序算法(11)——计数排序算法详解

    计数排序(Counting sort)是一个非基于比较的排序算法,该算法于1954年由 Harold H. Seward 提出.它的优势在于在对一定范围内的整数排序时,它的复杂度为Ο(n+k)(其中k ...

  4. c ++递归算法数的计数_计数排序算法–在C / C ++中实现的想法

    c ++递归算法数的计数 What is the counting sort algorithm? In Computer Science, sorting algorithms form the b ...

  5. 理解计数排序算法的原理和实现

    计数排序(Counting sort)是一种稳定的线性时间排序算法,其平均时间复杂度和空间复杂度为O(n+k),其中n为数组元素的个数,k为待排序数组里面的最大值.同样具有线性时间排序的算法还有桶排序 ...

  6. c++之计数排序算法

    1.计数排序   计数排序(Counting sort) 是一种稳定的排序算法.计数排序使用一个额外的数组C,其中第i个元素是待排序数组A中值等于i的元素的个数.然后根据数组C来将A中的元素排到正确的 ...

  7. 算法——计数排序与快速排序

    计数排序是一种算法复杂度 O(n) 的排序方法,适合于小范围集合的排序.比如100万学生参加高考,我们想对这100万学生的数学成绩(假设分数为0到100)做个排序.我们如何设计一个 最高效的排序算法. ...

  8. 排序算法:桶排序、计数排序、基数排序

    相关博客: 排序算法:冒泡排序.插入排序.选择排序.希尔排序 排序算法:归并排序.快速排序 排序算法:桶排序.计数排序.基数排序 排序算法:堆排序 十大排序算法小结 这篇博客将主要介绍三种时间复杂度是 ...

  9. 倒序排序_排序算法(六):Counting Sort 计数排序

    之前文章介绍的一些排序算法都是基于比较来进行排序的,故它们在平均情况下的时间复杂度最好也不过是线性对数级别.这里我们来介绍一种简单的基于非比较的排序算法--Counting Sort 计数排序,其时间 ...

最新文章

  1. 如何读取csv文件中第n行数据python-python数据处理之如何选取csv文件中某几行的数据...
  2. windows下安装RabbitMQ
  3. mybatis实现分页查询-自己封装分页方法
  4. VScode同步配置和插件步骤
  5. LeetCode 6 - ZigZag Conversion
  6. mysql 处理一条语句卡死_一条MySQL查询语句,卡死机器,不知道为什么,求高手指点!...
  7. Java面试知识点手工笔记(推荐)(转)
  8. hasOwnProperty()方法与in操作符
  9. 【时间序列分析】01.时间序列与平稳序列
  10. 前端成长之路之打好根基
  11. 天下足球 2016年欧洲杯 化茧成蝶 文案整理
  12. 【typescript】infer的理解与使用
  13. 基于 图神经网络 + 知识图谱 的推荐系统 1
  14. git拉取报错:You have not concluded your merge. (MERGE_HEAD exists)
  15. blender关于怎么导出上完色的FBX格式模型、以及导出后再导入,材质预览和渲染模式下材质透明度混乱的解决办法。
  16. 新海诚动漫《天气之子》1080P 4K下载
  17. 青春三部曲(《且听风吟》,《一九七三年的弹子球》,《寻羊冒险记》)--[日]村上春树...
  18. shell脚本之双重循环
  19. java 水印排版_java图片加水印代码 最好有实例!!!先谢了!!
  20. 分享一些我的远程办公经验

热门文章

  1. GITHUB应该知道的英文缩写
  2. SV基础知识4---Class类和Package
  3. 商业智能BI行业分析思维框架:铅酸蓄电池行业(一)
  4. 解决 Centos7 下中文显示乱码
  5. 苹果系统自带的计算机怎么恢复出厂设置方法,Mac电脑初始化、恢复出厂设置方法...
  6. python——fractions模块
  7. SMT贴片打样中常出现“立碑”的原因有哪些?
  8. 怎么把服务器里面的微信拉到群里,把14亿中国人都拉进一个微信群里会有怎样的盛况?...
  9. 计算机四级 网络工程师 考过指南,2015计算机四级考试报考指南:考试简介
  10. 项目背景以及游戏平台简介