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. 光环国际的PRINCE2培训时间

    一.光环国际的PRINCE2课程安排培训方式:    小班授课,50人为限;   全国网址直播课程,覆盖各个地区学员    精读原理配合独家开发大量实际案例研讨;    从商业战略角度解析PRINCE ...

  2. Oracle14~23

    14.查询所有学生的Sname.Cno和Degree列. 15.查询所有学生的Sno.Cname和Degree列. 16.查询所有学生的Sname.Cname和Degree列. 17. 查询“9503 ...

  3. tomcat配置管理员-走后门

    在Tomcat中,应用程序的部署很简单,只需将你的WAR放到Tomcat的webapp目录下,Tomcat会自动检测到这个文件,并将其解压.在浏览器中访问这个应用的Jsp时,通常第一次会很慢,因为To ...

  4. poptest老李谈Socket

    poptest是国内唯一一家培养测试开发工程师的培训机构,以学员能胜任自动化测试,性能测试,测试工具开发等工作为目标.如果对课程感兴趣,请大家咨询qq:908821478,咨询电话010-845052 ...

  5. 4.Linux的文件搜索命令

    1.文件搜索命令  which 语法:which [命令名称] 范例:$which ls  列出ls命令所在目录 [chanshuyi@localhost ~]$ which ls alias ls= ...

  6. UIScrollView的布局

    一.UIScrollView的子控件布局不能跟其它的控件一样进行布局,因为UIScrollView会根据子控件计算出ContentSize的大小,那么我们应该如何进行布局呀. 遵循以下两点就行 1.参 ...

  7. MongoDB基础教程系列--第七篇 MongoDB 聚合管道

    在讲解聚合管道(Aggregation Pipeline)之前,我们先介绍一下 MongoDB 的聚合功能,聚合操作主要用于对数据的批量处理,往往将记录按条件分组以后,然后再进行一系列操作,例如,求最 ...

  8. IC卡读卡器在安卓(android)下的开发

    友我科技推出IC卡读卡器的Android开发包,软件工程师只需要导入jar类库文件,就可以在App中操作IC卡读卡器了. 目前IC卡读卡器的Android开发包开放操作的卡类型有:M1, S50, U ...

  9. javascript核心概念之——数组

    在javascript中数组就是一个可以存放任何类型的集合.存储在数组中的值用逗号分隔 var arr = ["hello",7,null,undifined,obj,undifi ...

  10. Java环境变量详解

    自己总结些再加抄点: 安装JDK后要配置环境变量,主要有三个: 1 JAVA_HOME ->为JDK的安装目录,如:F:\JAVA\jdk1.6.0_04 2 CLASSPATH ->到哪 ...