数据结构 - 直接插入排序法。

之前的博文已经介绍了 冒泡排序法
和 简单选择排序法.

其实上面两种的基本思路是一样的, 就是通过两层循环, 在每1个内循环中找到1个未排序的极值元素, 然后把这个元素移动到数组前列。

但是这篇文章介绍的 直接插入排序法 的基本思路则与上面的排序方法不同, 而且可以讲 直接插入排序法 的算法更加直接人类大脑的思路。

基本思路

一个例子

假设我们面前有几张牌

|1|2|3|7|6|5|9|8|

要让你移动牌的位置, 让这些牌从小到达重新排列。

让我们模拟你的大脑行为…

首先, 我们一样就看出前面的|1|2|3|是排好序的, 不需要比较和移动。

其次, 我们不会去考虑剩下未排序中的牌哪张是最小的。

而是会去观察, 咦? 发现6比7大窝~

就去将牌6 放到 7的前面, 牌就变成这样了:

|1|2|3|6|7|5|9|8|

然后继续, 又发现5比7小, 就把5放到前面, 放到哪个牌前面呢, 直觉上我们会觉得6比5大, 所以放在6 前面:

|1|2|3|5|6|7|9|8|

下一步, 我们发现9比7大…不移动, 而是把后面的8移动到9前面。

|1|2|3|5|6|7|8|9|

好了, 区区几步就完成了排序, 是不是觉得我们的大脑很强大?

思路分析

但是, 如果我们要用代码去模拟我们大脑的算法,就必须了解清楚这个算法的每1个步骤细节。

1.确定开始比较的牌

首先, 我们一开始就跳过了1 2 3 这3张牌, 直接去比较后面那两张牌7和5。
是因为我们觉得前面的123 已经拍好序了。

但是前面存在若干个排好序的元素是这个排序方法的前置条件吗?

不是的,

假如我们去掉牌123, 变成如下这样子:

|7|6|5|9|8|

还是可以用这个思路来排序的。 因为我们可以当第1个元素7, 把它当成1个已经排好序的队列.

所以, 实际比较是从队列第二张牌6开始的。

也就是将, 这个思路中, 如果有1个数组 arr[n], 比较从第二个元素arr[1]

2.跟谁比较

接下来继续讲, 我们拿牌6 来 比较了,跟谁比较呢, 是跟6前面的牌7来比较。

也就是讲, 如果我们比较的元素是arr[i], 那么会跟它前面的元素arr[i-1]来比较。

3.如果arr[i] 比 arr[i-1] 大

为了方便表述我们把数组改成如下这个状态

|1|2|3|6|7|5|9|8|

我们正在比较的元素是7, 发现7比6小的情况下, 是不会移动任何牌的位置的, 而是去比较一下张牌5.

也即是将, 如果arr[i] > arr[i-1]的话, 直接i++ 去较下一张牌

4.相反,如果arr[i] 比 arr[i-1] 小

现在我们比较的是5 和 它前一位的7

|1|2|3|6|7|5|9|8|

这种情况下就要改变 元素的位置了。
我们会把牌5往前面放, 但是放在哪里呢, 而且怎么放呢。 还继续要分析

5. 先把要前移的牌拿出来

队列就变成这样子

|1|2|3|6|7|*|9|8|

|5|

6. 前面的牌往后一1个位置。

实际上, 5前面的 6和7 都要往后移动1个位置, 为什么3不移动呢, 因为3比5小啊

分两步(实际上是1个循环)

|1|2|3|6|7|*|9|8| ==>

|1|2|3|6|*|7|9|8| ==>

|1|2|3|*|6|7|9|8|

7. 拿出来的牌放回去

|1|2|3|5|6|7|9|8|

8. 继续比较下一张牌

|1|2|3|5|6|7|9|8|

9.小结

可以看出, 这个算法还是有两个循环的。

外循环用于比较元素和它前1个元素。

内循环实际上是移动1个子队列。

代码实现

了解思路后, 用c语言写下代码是不难的

int straightInsertionSort(int * arr, int len){int i, j;int tmp = 0;for (int i = 1; i < len; i++){if (arr[i-1] > arr[i]){ //1. the element will be moved only if it's smaller then the previous one//2. the element only will be moved forward.tmp = arr[i]; //use a variable to store the element which will be moved.j = i - 1;do {arr[j + 1] = arr[j];         //move the previous element one position backward fisrt;j--;}while (j >= 0 && arr[j] > tmp);  // all the elements after the new position of the moving element move!arr[j + 1] = tmp;                //put the moving element to the new position}}return 0;
}

debug信息

我们来看下 debug信息

4, 1, 5, 8, 0, 3, 7, 9, 6, 2
*1, 4, 5, 8, 0, 3, 7, 9, 6, 2
*0, 1, 4, 5, 8, 3, 7, 9, 6, 2
0, 1, *3, 4, 5, 8, 7, 9, 6, 2
0, 1, 3, 4, 5, *7, 8, 9, 6, 2
0, 1, 3, 4, 5, *6, 7, 8, 9, 2
0, 1, *2, 3, 4, 5, 6, 7, 8, 9
change count: 25
compare count: 22
0, 1, 2, 3, 4, 5, 6, 7, 8, 9

可以看出元素是怎样被移动的。
这个算法比较了20次, 位置移动了25次

首先, 这个算法比较次数相当少, 同1个数组, 用冒泡排序法的比较次数是39哦, 而简单选择法的比较次数是45.

位置移动是25, 跟冒泡还多吗。

不, 冒泡的位置改动实际上是元素交换(移动两个元素), 而这里的位置改动真的是1个元素的位置改动!

可以看出, 直接插入选择法比冒泡排序法具有更高的效率。
实际上, 比起简单选择法也是更好的

时间复杂度分析。

首先 最好的情况, 就是已经排好序的, 只需要比较n次, 无任何元素移动, 时间复杂度是O(n)

但是, 最坏打情况下的话

就是, 数组本身是按倒序排列的例如

|5|4|3|2|1|

那么, 比较次数是
∑i=1n−1i=1+2+3+4+...+n−1=n(n−1)2\sum\limits_{i=1}^{n-1}i= 1 + 2 + 3 + 4 + ... + n -1 = \frac{n(n-1)}{2}

移动次数是
∑i=2n−1i=2+3+4+...+n=n(n+2)2\sum\limits_{i=2}^{n-1}i= 2 + 3 + 4 + ... + n = \frac{n(n+2)}{2}

时间复杂度仍然是O(n2)O(n^2) 哦。

小结

我们又知道了1个更具效率的排序法。

关键是,这个直接插入排序法的思路更加符合人脑习惯。

可以看出, 容易写出的算法(双重排序法)并不一定是人类大脑最直觉的算法!

数据结构 - 直接插入排序法相关推荐

  1. 经典算法之折半插入排序法

    活动地址:21天学习挑战赛 文章目录 一.算法 1.算法概述 2.算法步骤 二.算法实践 1.Java代码 2.执行结果 三.复杂度分析 1.时间复杂度 2.空间复杂度 一.算法 1.算法概述 直接插 ...

  2. 插入排序法(思路及代码实现)

    插入排序法思想: 插入排序的基本思想是:把n个待排序的元素看成一个有序表和一个无序表,开始时有序表只包含一个元素,无序表中包含有n-1个元素,排序过程中每次从无序表中取出第一个元素,把它的排序码依次与 ...

  3. 插入排序法算长度为10的数组

    插入排序法的原理: 通过构建有序序列,对于未排序数据,在已排序序列中从后向前扫描,找到相应的位置并插入.对于给定的一组记录,初始时假定第一个记录自成一个有序序列,其余记录为无序序列.接着从第二个记录开 ...

  4. 数据结构---简单插入排序

    数据结构-简单插入排序 原理:参考趣学数据结构 代码: #include<stdio.h> #include<stdlib.h> void simpleInsertSort(i ...

  5. python中怎么比较两个列表的大小_python中对列表元素大小排序(冒泡排序法,选择排序法和插入排序法)—排序算法...

    前言 排序(Sorting) 是计算机程序设计中的一种重要操作,它的功能是将一个数据元素(或记录)的任意序列,重新排列成一个关键字有序的序列.本文主要讲述python中经常用的三种排序算法,选择排序法 ...

  6. 数据结构--直接插入排序

    直接插入排序 插入排序(Insertion Sort)的基本思想是:每次将一个待排序的记录,按其关键字大小插入到前面已经排好序的子文件中的适当位置,直到全部记录插入完成为止.     本节介绍两种插入 ...

  7. 排序算法(一)--插入排序法折半插入排序法

    约定: 假设数据中有n个数据元素(关键字).排列算法中,将序列中各关键字值依次存放于类型为keytype的数组元素K[1], K[2], K[3], -, K[n]中. 排序结果按照数据元素(关键字) ...

  8. java插入法排序_java算法之插入排序法

    思想:插入排序法的思想就是从数组的第二个元素开始,将数组中的每一个元素按照规则插入到已排好序的数组中以达到排序的目的.一般情况下将数组的第一个元素作为启始元素,从第二个元素开始依次插入.由于要插入到的 ...

  9. 简单排序(插入排序法)

    蒜头君给了一个长度为 N(不大于 500)的正整数序列(正整数的值不超过 N),请将其中的所有奇数取出,并按升序输出. 输入格式 共 2 行: 第 1行为 N: 第 2 行为 N 个正整数,其间用空格 ...

最新文章

  1. 「面试题」介绍你做过最复杂的系统
  2. 华为防火墙ftp_FTP被动模式访问问题
  3. .NET Core 3.1通用主机原理及使用
  4. hexo 环境变量_小白使用 Github + Hexo 从 0 搭建一个博客
  5. Lambda 表达式详解~Stream Pipelines
  6. 全排列及相关扩展算法(三)——利用中介数求排列在字典序排位算法
  7. 面试精讲之面试考点及大厂真题 - 分布式专栏 21 限流不能乱用-系统限流最佳实践
  8. Conditional Jump Instructions
  9. 详述近期遭利用的 Atlassian Confluence OGNL 注入漏洞 (CVE-2021-26084)
  10. 主引導记录(MBR)
  11. 批量拿php webshell,剑眉大侠:批量入侵网站拿webshell挂广告引流!
  12. APM —全链路追踪
  13. 智慧家安监控系统——用Java + html、css、js实现
  14. 飞塔防火墙VOIP ALG参数导致SIP外呼无声音的坑
  15. Zabbix实现企业微信报警
  16. node 插件下载 安装 数据库引入
  17. uni-app获取当前时间日期及之后7天和星期几
  18. 如何将PDF文档转成JPEG/JPG格式?
  19. 记住c0000005 Access Violation 崩溃
  20. Macbook系统清理的两种方式

热门文章

  1. controller-runtime 控制器实现
  2. 【Opencv实战】图像修复神技?看我一秒修复家里的老照片
  3. [ARM异常]-ARMV8的异步异常(中断)详细介绍
  4. 程序人生 Hello‘s P2P
  5. linux编译llvm代码
  6. 【网络安全】反序列化漏洞底层扩展与制作WebShell
  7. C++判断exe是32位还是64位
  8. 010 自动技能的设计和实现
  9. 160个Crackme020之无OD爆破
  10. ANSI,ASCII,UNICODE