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. sass或scss入门

    1.sass环境搭载: 安装ruby 安装sass 安装compass 配置webstorm 如果只是使用sass的话,就配置sass命名监听就好了 如图: sass目录如下: 如果配置了compas ...

  2. Quartz.NET总结(六)了解Jobs 和 Triggers

    前面讲了一些Quartz.net 的一些东西, http://www.cnblogs.com/zhangweizhong/category/771057.html 但是发现有一些基础的东西,没有说到. ...

  3. 老李分享:robotium常用API 1

    老李分享:robotium常用API   robotium常用API Robotium中最重要的类Solo类:其中提供了模拟用户活动的点击.搜索.拖拽等方法. 英文api(经常断开连接打不开):htt ...

  4. 【Direct2D开发】 通过操作像素实现纹理混合

    转载请注明出处:http://www.cnblogs.com/Ray1024 一.概述 我们都知道Direct2D可以加载并显示图片,但是不知道你有没有想过,这个2D的图形引擎可以进行纹理混合吗?如果 ...

  5. 爬楼梯问题-斐波那契序列的应用.md

    N 阶楼梯,一次可以爬1.2.3...n步,求爬楼梯的种类数 /** * 斐波那契序列 */ public class ClimbingStairs { // Sol 1: 递归 // 递归 公式:F ...

  6. ROS使用常见问题

    1.Q:查看ros版本 A:先在终端输入roscore,打开新终端,再输入,rosparam list,再输入rosparam get /rosdistro,就能得到版本. 2.Q:运行命令$ ros ...

  7. 快速排序算法回顾 --冒泡排序Bubble Sort和快速排序Quick Sort(Python实现)

    冒泡排序的过程是首先将第一个记录的关键字和第二个记录的关键字进行比较,若为逆序,则将两个记录交换,然后比较第二个记录和第三个记录的关键字.以此类推,直至第n-1个记录和第n个记录的关键字进行过比较为止 ...

  8. C/C++常考面试题(一)

    这算是一个系列吧,记录一下在准备秋招期间,所准备的C++面试题,望秋招顺利.所有的面试题均来源于各大论坛,网络. C/C++常考面试题(一) 常用的C++数据结构有哪些? vector,序列式容器,相 ...

  9. 基于appium的移动端自动化测试,密码键盘无法识别问题

    基于appium做自动化测试,APP密码键盘无法识别问题解决思路 这个问题的解决思路如下: 1.针对iOS无序键盘:首先,iOS的密码键盘是可识别的,但是,密码键盘一般是无序的.针对这个情况,思路是用 ...

  10. Linux防火墙配置—访问外网WEB

    一.实验目标 1.本次实验在"Linux基础网络搭建实验"的基础上,在外网虚拟机上搭建WEB服务,并分别配置外网和网关的防火墙规则,使内网能够访问WEB服务 2.Linux基础网络 ...