文章图片存储在GitHub,网速不佳的朋友,请看《基础排序算法详解与优化》 或者 来我的技术小站 godbmw.com

1. 谈谈基础排序

常见的基础排序有选择排序、冒泡排序和插入排序。众所周知,他们的时间复杂度是 O(n*n)。

但是,现在要重新认识一下基础排序算法,尤其是“插入排序”:在近乎有序的情况下,插入排序的时间复杂度可以降低到 O(n)的程度。

因此,在处理系统日志的任务中,因为日志记录是按照时间排序,但偶尔会有几条是乱序,此时使用插入排序再好不过。而对于高级排序算法,一个常见的优化就是利用插入排序做局部数据排序优化。

2. 算法实现

排序算法被封装在了SortBase.h中的SortBase命名空间中,以实现模板化和防止命名冲突。如下图所示:

2.1 选择排序

假设从小到大排序,那么,刚开始指针指向第一个数据,选择从当前指针所指向数据到最后一个数据间最小的数据,将它放在指针位置。

指针后移一位,重复上述步骤,直到指针移动到最后一个数据。

这种重复保证了每次,指针前面的数据都是从小到大排好顺序的数据。所以,从头到尾扫描一遍,自然排好序了。

代码如下:

template <typename T>
void selectionSort(T arr[], int n) {int minIndex = -1;for(int i = 0; i < n; i++) {minIndex = i;for(int j = i+1; j < n; ++j) {if(arr[j] < arr[minIndex]) {minIndex = j;}}swap(arr[i], arr[minIndex]);}
}

2.2 冒泡排序

假设排序是从小到大排序。

我一直感觉冒泡排序是和选择排序反过来了(如果说错请指正)。因为选择排序是每次选择最小的数据,放到当前指针位置;而冒泡排序是把不停交换相邻数据,直到把最大的数据“冒泡”到应该到的位置。

优化的地方是:记录每次交换的最后位置,在此之后的元素在下一轮扫描中均不考虑。因为交换的最后位置之后的元素已经是从小到大排序好了的。

在实现过程中,因为需要不停交换相邻两个数据,因此,消耗了很多额外时间。

template <typename T>
void bubbleSort(T arr[], int n) {int newn;do {newn = 0;for(int i = 1; i < n; i++) {if(arr[i-1] > arr[i]) {swap(arr[i-1], arr[i]);// 优化newn = i;}}n = newn; // 不再考虑 newn 后的数据} while (newn > 0);
}

2.3 插入排序

插入排序容易和上面两个算法搞混。可以类比打扑克牌时候的对扑克牌进行排序:我们会先排序前 1 张、然后是前 2 张、前 3 张 ... 一直到前 n 张。算法实现显然是双重循环,如下所示:

template <typename T>
void insertionSort(T arr[], int n) {for(int i = 1; i < n; i++) {for(int j = i ; j > 0; j--) {if(arr[j - 1] > arr[j]) {swap(arr[j], arr[j - 1]);} else {break; // 优化:已经保证之前都是正常排序,直接跳出即可}}}
}

显然,插入排序也能在局部排好序的情况下跳出循环(代码中的优化),以减少算法消耗时间。

然而上述算法其实跑分并比不上选择排序,因为swap(arr[j], arr[j - 1]);这行代码交换了一次,相当于赋值 3 次,在大数据量情况下,比较消耗时间。

优化: 内层循环,每次保存arr[i], 在检测到当前数据大于arr[i]的时候,后移一位当前元素arr[j] = arr[j-1];。当跳出内层循环时,直接将保存的arr[i]赋值给arr[j]即可。

template <typename T>
void insertionSort(T arr[], int n) {for(int i = 1; i < n; i++) {T e = arr[i];int j = i ;for(; j > 0 && arr[j-1] > e; j--) {arr[j] = arr[j-1];}arr[j] = e;}
}

3. 性能测试

首先利用 SortTestHelper::generateRandomArray函数生成大量无序随机数据,然后进行排序和时间测定。代码如下:

#include <iostream>
#include "SortHelper.h"
#include "SortBase.h"
#include "SortAdvance.h"using namespace std;int main() {int n = 50000, left = 0, right = n;int *arr = SortTestHelper::generateRandomArray<int>(n, left, right);int *brr = SortTestHelper::copyArray<int>(arr, n);int *crr = SortTestHelper::copyArray<int>(arr, n);SortTestHelper::testSort<int>(arr, n, SortBase::selectionSort<int>, "selection sort");SortTestHelper::testSort<int>(brr, n, SortBase::insertionSort<int>, "insertion sort");SortTestHelper::testSort<int>(crr, n, SortBase::bubbleSort<int>, "bubble sort");delete[] brr;delete[] arr;delete[] crr;return 0;
}

运行结果如下图所示:

除了大量无序随机数据,类似于系统日志的数据就是基本有序的大量数据。此时,测试代码如下:

#include <iostream>
#include "SortHelper.h"
#include "SortBase.h"
#include "SortAdvance.h"using namespace std;int main() {int n = 50000, left = 0, right = n;int *arr = SortTestHelper::generateNearlyOrderedArray<int>(n, 10);int *brr = SortTestHelper::copyArray<int>(arr, n);int *crr = SortTestHelper::copyArray<int>(arr, n);SortTestHelper::testSort<int>(arr, n, SortBase::selectionSort<int>, "selection sort");SortTestHelper::testSort<int>(brr, n, SortBase::insertionSort<int>, "insertion sort");SortTestHelper::testSort<int>(crr, n, SortBase::bubbleSort<int>, "bubble sort");delete[] brr;delete[] arr;delete[] crr;return 0;
}

如图所示,插入排序的只用了 0.002 秒。在这种数据情况下,插入排序的时间复杂度近似 O(N),绝对快于高级排序的 O(NlogN)。除此之外,还保证了稳定性。

4. 感谢

本篇博客是总结于慕课网的《学习算法思想 修炼编程内功》的笔记,liuyubobobo 老师人和讲课都很 nice,欢迎去买他的课程。

5. 更多内容

  • 快速排序和归并排序:《高级排序算法实现与优化》
  • 堆与堆排序:《堆、堆排序和优先队列的那些事》

基础排序算法详解与优化相关推荐

  1. 数据结构 7 基础排序算法详解 鸡尾酒排序法、了解钟摆排序实现

    前言 上节,我们已经通过对冒泡算法的优化.能够达到我们预想的结果.比较次数的减少.本节将继续在冒泡排序的基础上进行优化.能够达到刚好的效果. 鸡尾酒排序

  2. 1177: 按要求排序(指针专题)_数据结构 8 基础排序算法详解、快速排序的实现、了解分治法...

    快速排序 快速排序与冒泡排序一样,同样是属于 交换排序 叫做快速排序也是有原因的.因为它采用了 分治法的概念 其中最重要的一个概念就是 基准元素 冒泡排序每一轮将一个最大的元素挑选出并移动到右侧. 分 ...

  3. 7大排序算法详解+java实现

    目录 0 概述 1 冒泡排序 2 选择排序 3 插入排序 4 希尔排序 5 快速排序 6 归并排序 7 基数排序 下载地址 7大排序算法详解文档及java代码实现(可直接运行)下载地址:https:/ ...

  4. 十大经典排序算法详解

    本文转自 <卢明冬的博客> 文章目录 排序算法的分析和评价 2.十大排序经典算法总览 2.1 排序算法的分类 2.2 排序算法的性能 2.3 各阶复杂度性能对比 2.4 排序算法的初始状态 ...

  5. js排序算法详解-冒泡排序

    全栈工程师开发手册 (作者:栾鹏) js系列教程5-数据结构和算法全解 js排序算法详解-冒泡排序 1.1 原始人冒泡排序 function bubbleSort(arr) {var len = ar ...

  6. 排序算法,最全的10大排序算法详解(Sort Algorithm)

    文章目录 排序算法,最全的10大排序算法详解(Sort Algorithm) 排序算法分类 排序算法稳定性 时间复杂度(time complexity) 1#时间复杂度的意义 2#基本操作执行次数 如 ...

  7. JS 排序算法详解(冒泡排序,选择排序,插入排序,希尔排序,快速排序)

    JS 排序算法详解(冒泡排序,选择排序,插入排序,希尔排序,快速排序) 一. 大O表示法 在进行排序算法之前,我们得先掌握一种对算法效率的表示方法,大O表示法. 我们使用大O表示法来表示算法的时间复杂 ...

  8. 十大经典排序算法详解(三)-堆排序,计数排序,桶排序,基数排序

    养成习惯,先赞后看!!! 你的点赞与关注真的对我非常有帮助.如果可以的话,动动手指,一键三连吧!!! 十大经典排序算法-堆排序,计数排序,桶排序,基数排序 前言 这是十大经典排序算法详解的最后一篇了. ...

  9. 十大经典排序算法-桶排序算法详解

    十大经典排序算法 十大经典排序算法-冒泡排序算法详解 十大经典排序算法-选择排序算法详解 十大经典排序算法-插入排序算法详解 十大经典排序算法-希尔排序算法详解 十大经典排序算法-快速排序算法详解 十 ...

最新文章

  1. 机器学习入门书籍导读-工程高等代数
  2. linux asm 磁盘管理,asm磁盘管理篇
  3. 2. with check option能起什么作用?_面部毛孔粗大,有什么拯救的方法吗?
  4. SiteServer CMS 新版本 V6.15(2020年6月1日发布)
  5. 递归函数时间复杂度分析
  6. datx 开启debug
  7. JAVA-面向对象-多态
  8. mysql用正则表达式定位符_MYSQL使用正则表达式过滤数据
  9. linux加载和卸载驱动模块出现 'XXX': device or resource busy 错误提示
  10. 科研成果 | 信道模型 | 原理及随机数仿真 | 均匀、正态、双高斯、瑞利、莱斯、对数正态、nakagami、Suzuki分布的随机数仿真(matlab)
  11. php面试题目100及最佳答案,2020最新PHP面试100题(一)
  12. 中职双师型教师计算机培训总结,双师型教师计算机培训心得体会.doc
  13. 怎样才能批量查询网站的谷歌PR权重?把手教你批量查询网站谷歌PR权重值
  14. springboot基于spring的宽带管理系统以及实现毕业设计源码250910
  15. 2023最新小额现金贷系统源码+可打包成APP/内附安装教程
  16. 10小时入门大数据视频教程
  17. 首席新媒体运营黎想教程:活动运营避坑指南,促销活动的正确使用
  18. X61T L7500割脉超频成功!
  19. 战争雷霆steam正在连接服务器,战争雷霆玩steam好还是腾讯
  20. 分享一个抖音刷视频的python代码

热门文章

  1. 阿里巴巴 Java 开发手册之编程规约(一)-------我的经验
  2. 比较MongoDB在公有云上的性能:AWS、Azure和Digital Ocean
  3. 【转】【C#】C#重绘windows窗体标题栏和边框
  4. Delphi解析类似\u97e9这样的Unicode字符串
  5. windows 10 +fedora双系统引导修复
  6. 阿里云Redis多线程性能提升思路解析
  7. 18-python基础7-闭包函数和装饰器
  8. Spring+Shiro+CAS整合配置笔记
  9. Install ArchLinux on SSD
  10. CISCO7200路由器MultiChannel配置介绍