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. Lvs工作原理

    DR模式的工作过程: 当一个client发送一个WEB请求到VIP,LVS服务器根据VIP选择对应的real-server的Pool,根据算法,在Pool中选择一台Real-server,LVS在ha ...

  2. Visual Studio Code 使用Chrome Debug 代码

    一.添加插件 Debugger for Chrome,点击安装,安装完成之后,启动 二.配置启动参数 1.按 F5,出现界面如图,选择 Chrome 2.然后会打开配置文件 launch.json 3 ...

  3. 【Egret】在WebStorm里使用Egret Engine 的注意点

    1.开启代码提示 2.修改egret code后,自动编译新egret code 按照下图进行设置: ①打开"File-settings" ② ③ (PS:webstorm打开目录 ...

  4. 使用 SLF4J + LogBack 构建日志系统(转)

    转载自:http://www.cnblogs.com/mailingfeng/p/3499436.html 上次我们讨论了如何选择一个好的开源日志系统方案,其中的结论是:使用 SLF4J + LogB ...

  5. 2.熟悉Java基本类库系列——Java IO 类库

    Java中常用的IO操作基本上可以分为四大部分,分别是:File类操作.RandomAccessFile类操作.字节流操作.字符流操作.只要熟练掌握了本文中所列举的所有例子,基本上对于Java的IO流 ...

  6. 腾讯IVWEB团队:WebRTC 点对点直播

    作者:villainthr 摘自:villainhr WebRTC 全称为:Web Real-Time Communication.它是为了解决 Web 端无法捕获音视频的能力,并且提供了 peer- ...

  7. JavaWeb总结(六)—Session

    一.Session的介绍 在Web开发中,服务器可以为每个用户浏览器创建一个会话对象(session对象),注意:一个浏览器独占一个session对象(默认情况下).因此,在需要保存用户数据时,服务器 ...

  8. XML查询

    XPath是XML的查询语言,其内容相当复杂.可以查阅www.w3.org/TR/xpath. 下面以一个实例简单了解一线XPath的查询方法: public partial class Form1 ...

  9. 混合式app ionic2.x 手动搭建开发环境教程分享(nodejs,jdk,ant,androidsdk)

    1.ionic简介 为什么选用ionic: 彻底开源且免费 性能优异 基于红的发紫的AngularJs 漂亮的UI 强大的命令行(基于更热门的nodejs) 开发团队非常活跃 ngCordova,将主 ...

  10. Java线程池ThreadPoolExecutor使用和分析(三) - 终止线程池原理

    相关文章目录: Java线程池ThreadPoolExecutor使用和分析(一) Java线程池ThreadPoolExecutor使用和分析(二) - execute()原理 Java线程池Thr ...