直接插入排序(插入排序)

排序思想

  • 对于一个数组 A[0,n] 的排序问题,假设认为数组在 A[0,n-1] 排序的问题已经解决了。
  • 考虑 A[n] 的值,从右向左扫描有序数组 A[0,n-1] ,直到第一个小于等于 A[n] 的元素,将 A[n] 插在这个元素的后面。

插入排序运用时需要注意的

直接插入排序对于 最坏的情况(严格递减/递增的数组),需要比较和移位的次数为 n(n-1)/2

对于 最好的情况(严格递增/递减的数组),需要比较的次数是 n-1 ,需要移位的次数是 0

直接插入排序适用于 顺序存储链式存储结构 的线性表。注意,为链式存储时,可以从前往后查找指定元素的位置。一般来说,当 数据规模较小 的情况下,可以考虑使用直接插入排序

插入排序算法分析

直接插入排序的时间复杂度是 O(n^2),空间复杂度是 O(1),同时也是 稳定排序

稳定排序:待排序的记录序列中可能存在两个或两个以上关键字相等的记录。排序前的序列中Ri领先于Rj(即i<j).若在排序后的序列中Ri仍然领先于Rj,则称所用的方法是稳定的。

比如int数组[1,1,1,6,4]中a[0],a[1],a[2]的值相等,在排序时不改变其序列,则称所用的方法是稳定的。

代码实现

class Solution{
public:void insertSortion(vector<int> &nums) {if(nums.size() < 2) return;for(int i = 1; i < nums.size(); i++) {int num = nums[i];// 防止插入的数是最小的 bool flag = false;// for循环中,若插入值是最小的,没有给最小的值安排位置 for(int j = i - 1; j > -1; j--) {if(nums[j] > num) {nums[j+1] = nums[j];} else {nums[j+1] = num;flag = true;break;}}if(!flag) {nums[0] = num; }}return;}
};

加工后执行的结果

折半插入排序(插入排序)

排序思想:折半插入排序的改进版

  1. 有一组数列待排序,排序区间为 Array[0, n-1] 。将数列分为有序数列和无序数列,第一次排序时默认 Array[0] 为有序数列,Array[1, n-1] 为无序数列。有序数列分区的第一个元素位置为 low ,最后一个元素的位置为 high
  2. Array[0, i-1] 为有序数列,待插入数据为 Array[i]。由于 Array[0, i-1] 有序,使用对有序数列的查找,最好的方法时时间复杂度为 O(logn) 的折半查找,通过折半查找找到插入位置(即 low > high 时),将该位置及之后的数据分别往后挪一位

折半插入排序运用时需要注意的条件和直接插入排序时需注意的条件一样

折半插入排序对于 最坏的情况(严格递减/递增的数组),需要比较和移位的次数为 n(n-1)/2

对于 最好的情况(严格递增/递减的数组),需要比较的次数是 n-1 ,需要移位的次数是 0

但相比直接插入排序,折半插入排序在 查找位置的时候得到优化(O(n)O(logn)

折半排序算法分析

折半插入排序的时间复杂度是 O(n^2),空间复杂度是O(1),同时也是 稳定排序

代码实现

#include <stdio.h>
#include <vector>
using namespace std;
class Solution{
public:void binaryInsertionSort(vector<int> &nums) {if(nums.size() < 2) return;for(int i = 1; i < nums.size(); i++) {int low = 0;int high = i;int num = nums[i];while(low <= high) {int mid = (low + high) / 2;if(nums[mid] > num) {high = mid - 1;} else {low = mid + 1;}}int j;for(j = i - 1; j > high; j--) {nums[j+1] = nums[j];}nums[j+1] = num; }return; }
};

加工后执行的结果

希尔排序(插入排序)

排序思想:通过粗调的方式减少了直接插入排序的工作量

对于 Array[0,n-1] 这个数列有 n 个待排序的数字,取一个小于 n 的整数 stepstep被称为步长)将待排序元素分成若干个组子序列,所有距离为 step 的倍数的记录放在同一个组中;然后,对各组内的元素进行直接插入排序。 这一趟排序完成之后,每一个组的元素都是有序的。然后减小 step 的值,并重复执行上述的分组和排序。重复这样的操作,当 step=1 时,整个数列就是有序的。

希尔排序

希尔排序的时间复杂度与增量(即,步长 step )的选取有关。例如,当增量 step 为1时,希尔排序退化成了直接插入排序,此时的时间复杂度为 O(n²)

遇到极端情况,比如 step=4step=2 时,数组序列不变,进入 step=1 时,就是直接插入排序了,相比正常的直接插入排序,还多加了几次 step=4step=2 的判断

希尔排序仅适用于线性表为顺序存储的情况。

希尔排序算法分析

希尔插入排序的时间复杂度是 O(n^2),空间复杂度是 O(1) ,但希尔排序是 不稳定排序

代码实现

class Solution{
public:void ShellSort(vector<int> &nums) {int size = nums.size();int count = 0;for(int step = size / 2; step > 0; step /= 2) {for(int i = 0; i < step; i++) {insertSort(nums, size, i, step);}}}
private:void insertSort(vector<int> &nums, int size, int i, int step){for(int j = i + step; j < size; j+=step) {if(nums[j] < nums[j-step]) {int num = nums[j];int k = j - step;while(k >= 0 && nums[k] > num) {nums[k+step] = nums[k];k -= step;}nums[k+step] = num;}}return;}
};

加工后执行的结果

完整测试代码

直接插入排序测试代码

#include <stdio.h>
#include <vector>
using namespace std;
class Solution{
public:void insertSortion(vector<int> &nums) {if(nums.size() < 2) return;printf("第0轮:");for(int i = 0; i < nums.size(); i++) {printf("%d",nums[i]);if(i!=nums.size()-1) printf(",");}printf("\n");for(int i = 1; i < nums.size(); i++) {int num = nums[i];// 防止插入的数是最小的 bool flag = false;// for循环中,若插入值是最小的,没有给最小的值安排位置 for(int j = i - 1; j > -1; j--) {if(nums[j] > num) {nums[j+1] = nums[j];} else {nums[j+1] = num;flag = true;break;}}if(!flag) {nums[0] = num; }// 查看 printf("第%d轮:",i);for(int i = 0; i < nums.size(); i++) {printf("%d",nums[i]);if(i!=nums.size()-1) printf(",");}printf("\n");}return;}
};int main() {vector<int> v;v.push_back(23);v.push_back(76);v.push_back(34);v.push_back(89);v.push_back(90);v.push_back(34);v.push_back(56);v.push_back(18);Solution solution;solution.insertSortion(v);return 0;
}

折半插入排序测试代码

#include <stdio.h>
#include <vector>
using namespace std;
class Solution{
public:void binaryInsertionSort(vector<int> &nums) {if(nums.size() < 2) return;printf("第0轮:");for(int i = 0; i < nums.size(); i++) {printf("%d",nums[i]);if(i!=nums.size()-1) printf(",");}printf("\n");for(int i = 1; i < nums.size(); i++) {int low = 0;int high = i;int num = nums[i];while(low <= high) {int mid = (low + high) / 2;if(nums[mid] > num) {high = mid - 1;} else {low = mid + 1;}}int j;for(j = i - 1; j > high; j--) {nums[j+1] = nums[j];}nums[j+1] = num; // 查看 printf("第%d轮:",i);for(int i = 0; i < nums.size(); i++) {printf("%d",nums[i]);if(i!=nums.size()-1) printf(",");}printf("\n");}return; }
};int main() {vector<int> v;v.push_back(23);v.push_back(76);v.push_back(34);v.push_back(89);v.push_back(90);v.push_back(34);v.push_back(56);v.push_back(18);Solution solution;solution.binaryInsertionSort(v);return 0;
}

希尔排序测试代码

#include <stdio.h>
#include <vector>
using namespace std;
class Solution{
public:void ShellSort(vector<int> &nums) {int size = nums.size();int count = 0;// 查看 printf("第0轮:");count++;for(int i = 0; i < nums.size(); i++) {printf("%d",nums[i]);if(i!=nums.size()-1) printf(",");}printf("\n");for(int step = size / 2; step > 0; step /= 2) {for(int i = 0; i < step; i++) {insertSort(nums, size, i, step);}// 查看 printf("第%d轮:",count);count++;for(int i = 0; i < nums.size(); i++) {printf("%d",nums[i]);if(i!=nums.size()-1) printf(",");}printf("\n");}}
private:void insertSort(vector<int> &nums, int size, int i, int step){for(int j = i + step; j < size; j+=step) {if(nums[j] < nums[j-step]) {int num = nums[j];int k = j - step;while(k >= 0 && nums[k] > num) {nums[k+step] = nums[k];k -= step;}nums[k+step] = num;}}return;}
};int main() {vector<int> v;v.push_back(23);v.push_back(76);v.push_back(34);v.push_back(89);v.push_back(90);v.push_back(34);v.push_back(56);v.push_back(18);Solution solution;solution.ShellSort(v);return 0;
}

所有的插入排序均不是全局有序(全局排序:即每一轮均有一个数字的位置不会改变)

【数据结构-排序】1. 图解插入排序三种实现(插入排序/折半排序/希尔排序)相关推荐

  1. 图解虚拟机三种网络连接方式

    前言: 之前对这三种方式总是要用的时候现查资料,或者只要能满足访问外网的需求也就行了,没好好理解这三种方式,后来每次都要现查太浪费时间,所以自己总结一下: 二.虚拟机网络连接模式: 1. 桥接 2. ...

  2. 排序1:快速排序(三种)、归并排序、计数排序

    快排 1. hoare法 思路: 每次确定一个key[首元素]位置,从此分了两个区间:[0,key-1],[key+1,end]. 每个内部排序是双循环:取第一个为key,R先走,R找小,L找大,注意 ...

  3. 【数据结构】理解二叉树的三种遍历--前序、中序、后序 +层序(简明易懂)

    一.易懂的形象理解 其实从名字就可以很好的理解这三种遍历,我在第二点时候说,但是估计能翻到我的文的同学们之前肯定看过好多类似的了,那咱们换个思路~ 先用我想的一种简单易懂的形象思维理解一下前序.中序. ...

  4. Set排序的方式(常用三种,学会够用)

    Set排序的方式常用的一共有三种方式 在演示Set排序的代码前,要先了解 Set集合的特点: Set是个无序.不可重复的集合接口;有三个实现类: HashSet.TreeSet.LinkedHashS ...

  5. 希尔排序大详解,保证10分钟内学会希尔排序(进化版插入排序)

    希尔排序 核心思想是化远为近: 1.使查找次数减少 2.从而移动元素的次数减少 文章目录 希尔排序 动画引入 一.希尔排序的代码(两个函数实现) 二.希尔排序(一个函数实现) 总结 动画引入 提示:颜 ...

  6. 算法研究:插入类排序(简单插入,折半插入,希尔排序)

    百度百科:有一个已经有序的数据序列,要求在这个已经排好的数据序列中插入一个数,但要求插入后此数据序列仍然有序,这个时候就要用到一种新的排序方法--插入排序法,插入排序的基本操作就是将一个数据插入到已经 ...

  7. 下划线间隔数字 排序_面试必备:经典算法动画解析之希尔排序

    哈喽,我是程序员大鹏. 前面我们介绍了冒泡排序.选择排序和插入排序,今天我们来看一下进阶的排序. 1959年Shell发明,第一个突破O(n2)的排序算法,是简单插入排序的改进版.它与插入排序的不同之 ...

  8. java8 lambda 排序算法,Java8中排序算法比较器的三种写法(使用lambda表达式实现Comparator比较器)...

    在涉及到数组, 集合等这些地方经常会需要用到排序算法, 在Java中的Collections类中有sort方法, 除了需要传入一个Comparator比较器, 或者需要排序的类实现了Comparabl ...

  9. mybatis 中 Example 的使用 :条件查询、排序、分页(三种分页方式 : RowBounds、PageHelpler 、limit )

    前些天发现了一个巨牛的人工智能学习网站,通俗易懂,风趣幽默,忍不住分享一下给大家.点击跳转到教程. import tk.mybatis.mapper.entity.Example;import com ...

最新文章

  1. AI+DevOps正当时
  2. HTTP协议详解 转自小坦克
  3. calibre中的hcell_关于calibre的Hcell你知道多少?
  4. 如何搭建Electron开发环境
  5. 开机发现超级管理员账户不见了
  6. swagger2 分组
  7. app开发人脸登录和指纹登录_易讯云通讯推出“一键登录”,为App登录提供新方案...
  8. 【Redis】redis 主从复制
  9. java 执行代码超时,如何在Java中使用超时调用一些阻塞方法?
  10. 理解为什么要使用Ioc
  11. 【 PG 入门系列 】PostgreSQL的客户端工具(五)
  12. VMware12虚拟机怎么下载安装?保姆级安装教程,让你一分钟学会
  13. 天津大学网页设计与制作答案合集
  14. PHP实现倒计时插件代码
  15. html横线标记_html中横线怎么写代码
  16. 【苹果相册推】怎么操作呢?是什么意思?
  17. SurFS:共享式和分布式集群各取所长
  18. 戴尔R720系列服务器CPU升级,Dell 服务器R720的CPU列表
  19. 圆心科技再冲刺港交所上市:收入和亏损同增,毛利率走低,何涛为董事长
  20. su 命令无法切换用户

热门文章

  1. 【转】浅谈MS-SQL锁机制
  2. 基于汇编语言的电子琴设计(1)
  3. ESP32 SDK OTA Demo升级流程
  4. 什么时候需要用到RCC_APB2Periph_AFIO--复用IO时钟的使用
  5. html页面光标坐标值,javascript-在包含HTML内容的contentEditable区域中获取插入符(光标)的位置...
  6. 共识协议(3)比特币之POW
  7. CMM与CMMI的关系
  8. linux 静态编译 yara
  9. (55)_KPCR, _NT_TIB, _KPRCB
  10. (3)段描述符,段选择子,LES指令