O(n) 排序算法

前言

前面有总结过各类常用的排序算法,但是那些排序算法平均的时间复杂度是O(nlogn),所以我要介绍三种时间复杂度为O(n)的线性时间复杂度的排序算法。

计数排序

计数排序利用了哈希的性质,将一个中间数组来记录数值对应的下标,最后查询对应的下标进行放置;

步骤如下:

  1. 找出待排序的数组中最小和最大值,计算最大和最小值之间的差值;
  2. 计算每个数值出现的次数,接着进行累加计算出数值的位置;
  3. 反向填充数组,根据查询下标找到位置后填充数值;

实现

#include <iostream>
#include <vector>
using namespace std; vector<int> counting_sort(vector<int> nums) {
int max = nums[0], min = nums[0];
size_t len = nums.size();
for (size_t i = 1; i < len; i++) {
if (max < nums[i]) {
max = nums[i];
}
if (min > nums[i]) {
min = nums[i];
}
}
int k = max - min + 1;
vector<int> temp(k, 0);
// 第一步:计算每个数字出现的次数
for (size_t i = 0; i < len; i++) {
temp[nums[i] - min] += 1;
}
// 第二步:累加
for (size_t i = 1; i < len; i++) {
temp[i] += temp[i-1];
}
vector<int> result(len, 0);
// 第三步:将数字放在相应的位置
for (size_t i = 0; i < len; i++) {
result[--temp[nums[i] - min]] = nums[i];
}
return result;
} int main() {
vector<int> res = counting_sort({10, 9, 8, 7, 6, 5, 4, 3, 2, 1});
for (auto re : res) {
cout << re << " ";
}
return 0;
}

缺点和优点

利用了哈希的原理,其时间复杂度为n,但是这是用空间复杂度来换的,即便上面有进行过优化,但是面对一个较大值和较小值的数组,其仍然会对空间造成很大的浪费。

基数排序

将所有数值在每一位上面进行排序,排序方法利用计数排序的原理;

步骤:

  1. 计算数值中最大值的位数,用作后面比较的次数;
  2. 计算所有数值在每一位上面的排序,参考计数排序;

实现

void redis_sort(vector<int>& nums) {
int bits = max_bit(nums);
int len = nums.size();
vector<int> temp(len, 0), count(10, 0);
for (int i = 1, redix = 1; i <= bits; i++, redix *= 10) {
// 注意,每次分配前需要清空计数器
count.assign(10, 0);
// 第一步:计算每个数值下标出现的次数
for (int j = 0; j < len; j++) {
count[(nums[j]/redix)%10]++;
}
// 第二步:累加计算下标
for (int j = 1; j < 10; j++) {
count[j] += count[j-1];
}
// 第三步:根据bit的下标找到位置来填充
for (int j = len-1; j >= 0; j--) {
int k = (nums[j]/redix)%10;
temp[count[k]-1] = nums[j];
count[k]--;
}
// 第四部:排好序的数组赋值
for (int j = 0; j < len; j++) {
nums[j] = temp[j];
}
}
}

缺点和优点

因为其下标在0-10之间,所以有效的控制了空间复杂度,但是其复杂度较计数排序增加了,明显其时间复杂度为O(k * n),k代表数字位数,这取决于数字位的选择,比如比特位数,其决定了要进行多少轮的处理;虽然增加了时间复杂度,但依旧比那些需要进行比较的排序算法较快一些。

桶排序

桶排序的原理在于将数组分配到一定数量的桶中,每个桶在个别排序,最后合并排序。

实现

const int BUCKET_NUM = 10;

// 链表的插入排序
LinkNode* insert(LinkNode* head, int val) {
LinkNode *newhead = new LinkNode(0);
newhead->_next = head; LinkNode *node = new LinkNode(val);
LinkNode *temp = newhead;
while (temp->_next != NULL && temp->_next->_data <= val) {
temp = temp->_next;
}
node->_next = temp->_next;
temp->_next = node;
return newhead->_next;
} // 两个排序链表的合并
LinkNode* merge(LinkNode* head, LinkNode* bucket_node) {
LinkNode* newhead = new LinkNode(0);
LinkNode* temp = newhead;
while (head && bucket_node) {
if (head->_data > bucket_node->_data) {
temp->_next = bucket_node;
bucket_node = bucket_node->_next;
}
else {
temp->_next = head;
head = head->_next;
}
temp = temp->_next;
}
if (head != NULL) {
temp->_next = head;
}
else if (bucket_node != NULL) {
temp->_next = bucket_node;
}
return newhead->_next;
} vector<int> BucketSort(vector<int> nums) {
int len = nums.size();
vector<LinkNode*> buckets(BUCKET_NUM, (LinkNode*)(0));
// 第一步:对数值进行插入排序
for (int i = 0; i < len; i++) {
int idx = nums[i] % BUCKET_NUM;
LinkNode* head = buckets[idx];
buckets[idx] = insert(head, nums[i]);
}
// 第二步:将桶中的值进行合并
LinkNode *head = NULL;
for (int i = 0; i < BUCKET_NUM; i++) {
head = merge(head, buckets[i]);
}
// 第三步:将排序好的链表赋值
vector<int> result(len, 0);
for (int i = 0; i < len, head != NULL; i++, head = head->_next) {
result[i] = head->_data;
}
return result;
}

缺点和优点

如果数组中的每个数值都会均匀的落入每个桶中,则其最优的时间复杂度在n,但是如果数值都集中的加入到固定的几个桶中,甚至是都落入一个桶中,那么这样在对数值进行插入排序的时候就变成了双层循环,则其最差时间复杂度为n^2。

比较

o(n)线性排序算法的更多相关文章

  1. python实现线性排序算法-计数排序

    计数排序假定输入元素的每一个都是介于0到k之间的整数,此处K为某个整数,当k=O(n)时,计数排序的运行时间为O(n) 它的基本思想是:根据每个输入元素x确定小于x的元素个数,根据这个信息把x直接放到 ...

  2. 排序算法的C语言实现(下 线性时间排序:计数排序与基数排序)

    计数排序 计数排序是一种高效的线性排序. 它通过计算一个集合中元素出现的次数来确定集合如何排序.不同于插入排序.快速排序等基于元素比较的排序,计数排序是不需要进行元素比较的,而且它的运行效率要比效率为 ...

  3. Python实现各种排序算法的代码示例总结

    Python实现各种排序算法的代码示例总结 作者:Donald Knuth 字体:[增加 减小] 类型:转载 时间:2015-12-11我要评论 这篇文章主要介绍了Python实现各种排序算法的代码示 ...

  4. 用python实现各种排序算法

    最简单的排序有三种:插入排序,选择排序和冒泡排序.它们的平均时间复杂度均为O(n^2),在这里对原理就不加赘述了. 贴出源代码: 插入排序: def insertion_sort(sort_list) ...

  5. 十大排序算法总结(Python3实现)

    十大排序算法总结(Python3实现) 本文链接:https://blog.csdn.net/aiya_aiya_/article/details/79846380 目录 一.概述 二.算法简介及代码 ...

  6. 线性时间的排序算法--桶排序(以leetcode164. Maximum Gap为例讲解)

    前言 在比较排序的算法中,快速排序的性能最佳,时间复杂度是O(N*logN).因此,在使用比较排序时,时间复杂度的下限就是O(N*logN).而桶排序的时间复杂度是O(N+C),因为它的实现并不是基于 ...

  7. 模板化的七种排序算法,适用于T* vector<T>以及list<T>

    最近在写一些数据结构以及算法相关的代码,比如常用排序算法以及具有启发能力的智能算法.为了能够让写下的代码下次还能够被复用,直接将代码编写成类模板成员函数的方式,之所以没有将这种方式改成更方便的函数模板 ...

  8. 八大排序算法Java

    目录(?)[-] 概述 插入排序直接插入排序Straight Insertion Sort 插入排序希尔排序Shells Sort 选择排序简单选择排序Simple Selection Sort 选择 ...

  9. Java各种排序算法详解

    排序大的分类可以分为两种:内排序和外排序.在排序过程中,全部记录存放在内存,则称为内排序,如果排序过程中需要使用外存,则称为外排序.下面讲的排序都是属于内排序. 内排序有可以分为以下几类: (1).插 ...

随机推荐

  1. 性能测试分享:Jmeter多机协作原理

    性能测试分享:Jmeter多机协作原理   poptest是国内唯一一家培养测试开发工程师的培训机构,以学员能胜任自动化测试,性能测试,测试工具开发等工作为目标.如果对课程感兴趣,请大家咨询qq:90 ...

  2. WSDL/WebService/SOAP/REST/AXIS/CXF 开放式服务

    WebService是一种数据交换标准.通过WebService标准,你可以把项目中的方法作为接口提供给其他项目使用. 有时候我们习惯性地将具体提供服务的某个方法称为WebService.比如图书系统 ...

  3. EDP转换IC NCS8803:HDMI转EDP芯片

    HDMI-to-eDP Converter w/ scaler1 Features    Embedded-DisplayPort (eDP) Output    1/2/4-lane eDP @ 1 ...

  4. JavaScript基础学习(五)—其他引用类型

         JavaScript定义了两个内置对象: Global和Math. 一.Global对象 1.URI编码方法      Global对象的encodeURI()和encodeURICompo ...

  5. JavaWeb总结(二)—HttpServletResponse对象

    Web服务器收到客户端的http请求,会针对每一次的请求,分别创建一个用于代表请求的request对象和response对象.我们要获取客户端提交的数据,只需要找request对象.要向客户端输出数据 ...

  6. 对quartz定时任务的初步认识

    已经好久没有写技术博文了,今天就谈一谈我前两天自学的quartz定时任务吧,我对quartz定时任务的理解,就是可以设定一个时间,然后呢,在这个时间到的时候,去执行业务逻辑,这是我的简单理解,接下来看 ...

  7. Robotframe work学习之初(二)

    一.F5帮助 Robot Framework 并没有像其它框架一样提供一份完整的 API 文档,所以,我们没办法通过官方 API文档进行习.RIDE 提供了 F5 快捷键来打开帮助文档. search ...

  8. 优雅地解决Ajax接口参数来自另一个接口的问题

    最近闲赋在家,终于有时间回顾我在工作中遇到的一些东西,由于经验不足,有些方面做的不是很好.在上家公司曾经遇到一个小问题,就是Ajax的接口中有参数是从另一个接口后台传来的.当时我的做法是将需要参数的接 ...

  9. redis 字典

    redis 字典 前言 借鉴了 黄健宏 的 <<Redis 设计与实现>> 一书, 对 redis 源码进行学习 欢迎大家给予意见, 互相沟通学习 概述 字典是一种用于存储键值 ...

  10. JS的块级作用域

    今天带来的是 "对<你不知道的js>中块级作用域的总结" 分享: 1)用with从对象中创建出来的作用域只在with声明中而非外部作用域有效,同时可以访问已有对象的属性 ...