总算进入我们的排序相关算法的学习了。相信不管是系统学习过的还是没有系统学习过算法的朋友都会听说过许多非常出名的排序算法,当然,我们今天入门的内容并不是直接先从最常见的那个算法说起,而是按照一定的规则一个一个的介绍。

首先,我们要介绍的排序算法是插入类型的排序算法。顾名思义,插入排序就是将无序的一个或几个记录“插入”到有序的序列中,比较典型的例子就是简单插入排序和希尔排序。

简单插入排序

简单插入排序,也可以叫做直接插入排序。还是先看代码,再来进行下一步的解释。

function InsertSort($arr)
{
$n = count($arr);
for ($i = 1; $i < $n; $i++) { // 开始循环,从第二个元素开始,下标为 1 的
$tmp = $arr[$i]; // 取出未排序序列第一个元素
for ($j = $i; $j > 0 && $arr[$j - 1] > $tmp; $j--) { // 判断从当前下标开始向前判断,如果前一个比当前元素大
$arr[$j] = $arr[$j - 1]; // 依次移动元素
}
// 将元素放到合适的位置
$arr[$j] = $tmp;
}
echo implode(', ', $arr), PHP_EOL;
} InsertSort($numbers); // 49, 38, 65, 97, 76, 13, 27, 49
// 13, 27, 38, 49, 49, 65, 76, 97

代码量不多吧,其实也非常好理解。我们就拿测试数据的前两个数来简单地说明一下。

首先,第一个循环是从 1 开始的,也就是说,第一个取出的未排序序列元素是 tmp = arr[1] ,也就是当前的 tmp = 38 。

然后开始循环,当前的循环是判断 j-1 的元素是否比当前这个 tmp 元素大,如果是的话,进入循环体,arr[1] = arr[0] 。到目前为止,arr[0] 和 arr[1] 现在都是 49 。整个序列是 49,49,65,……

最后让 arr[0] = $tmp ,也就是等于 38 。(循环的时候 j-- 了)。整个序列是 38,49,65,……

通过下面这张图片,我们可以更清楚地看明白整个序列完成排序的过程。

从上面的步骤可以看出,简单插入排序就是从一边开始,先让前面的数据逐步有序的过程。从代码中就可以看出,它是不断地内部地循环中进行 j 的递减,与前面有序的数列进行比对,当发现了自己合适的位置之后,就将数据放到这个位置上。

从代码和我们的分析来看,简单插入排序的时间复杂度是 O(n2) 。同时,它是属于稳定的排序,什么叫稳定排序呢?细心的同学应该发现了,在我们的测试代码中,有两个相同的数据,也就是 49 。稳定的意思就是相同的数据在排序前后的位置不会发生改变,前面的 49 依然是在后面的 49 前面。这就是排序的稳定性。

另外,简单插入排序比较适合初始记录基本有序的情况,当初始记录无序,n 较大时,这个算法的时间复杂度会比较高,不太适合采用。

希尔排序

简单插入排序很好理解吧,希尔排序又是什么鬼呢?别着急,从这个名字我们是看不出什么端倪的,因为这个排序的名字是以它的发现者命名的。实际上,希尔排序还是一个插入排序的算法。

上文中说过,简单插入排序适合基本有序的情况,而希尔排序就是为了提升简单插入排序的效率而出现的,它主要目的是减少排序的 n 的大小以及通过几次排序就让数据形成基本有序的格式。

对于这个算法,我们不能先上代码了,先来看图吧。

看明白了吗?我们其实是将数据进行分组了,每次分组是以一定的增量为基础的,比如我们这个示意图中就是第一次以 5 为增量进行排序,第二次是以 3 为增量。这样第三次排序的时候,增量为 1 ,也就成为一个普通的简单插入排序了。一会我们代码中就会体现出来。

还是按增量为迭代次序进行这三趟排序的具体分析吧:

1)第一次迭代的时候,我们将分组增量设置为 5 ,这时分别有三组数据,也就是 49 和 13,38 和 27,65 和 49 ,然后对这三组数据进行简单插入排序,之后的数组结果是 13、27、49、97、76、49、38、65 。

2)第二次迭代,分组增量为 3,这时就分成了两组,每组三个数据,分别是 13、97、38 为一组,另一组是 27 、76 、65 。对这两组数据进行简单插入排序之后更新数组结果为 13、27、49、38、65、49、97、76 。

3)其实从两次分组排序之后就可以看出,这个数组已经基本有序了。这时最后就是以分组增量 1 再次进行简单插入排序。说白了,最后这一步就是一个普通的简单插入排序的过程了。

分步骤讲解之后是不是清楚很多了,再重复一篇,希尔排序其实就是按分组来一次大范围的插入排序,最后一步步缩小到只有 1 次增量的简单插入排序了。我们再来看看代码吧:

function ShellSort($arr)
{
$n = count($arr);
$sedgewick = [5, 3, 1]; // 初始的增量值不能超过待排序列的长度
for ($si = 0; $sedgewick[$si] >= $n; $si++); // 开始分组循环,依次按照 5 、3 、 1 进行分组
for ($d = $sedgewick[$si]; $d > 0; $d = $sedgewick[++$si]) {
// 获取当前的分组数量
for ($p = $d; $p < $n; $p++) {
$tmp = $arr[$p];
// 插入排序开始,在当前组内
for ($i = $p; $i >= $d && $arr[$i - $d] > $tmp; $i -= $d) {
$arr[$i] = $arr[$i - $d];
}
$arr[$i] = $tmp;
}
}
echo implode(', ', $arr), PHP_EOL;
}
ShellSort($numbers);

看着代码貌似有三层 for 循环呀,它哪里提升了效率了呢?其实希尔排序的效率提升确实是有限的,它其实是通过前几次的分组让数据先基本有序。而在分组的状态中,数据比较的数量并不会达到 n 的级别。当最后一次进行简单排序的时候,整个数据已经是基本有序了,在这种情况下交换的次数明显也会减少很多,所以它的时间复杂度在理想状态下可以减少到 O(log2n)2 的水平。

总结

排序的入门餐怎么样?我们可不是直接就拿烂大街的冒泡和快排上手的吧。不出名不意味着不会用到,比如我面试的时候曾经有个公司就是在面试题上写明了不能使用冒泡和快排。这时候,我相信简单插入排序直观好理解的特性一定就能帮助我们度过这种面试难关了哦!

测试代码:

https://github.com/zhangyue0503/Data-structure-and-algorithm/blob/master/7.排序/source/7.1插入类排序:简单插入、希尔排序.php

参考文档:

本文示例选自 《数据结构》第二版,严蔚敏

《数据结构》第二版,陈越

关注公众号:【硬核项目经理】获取最新文章

添加微信/QQ好友:【xiaoyuezigonggong/149844827】免费得PHP、项目管理学习资料

知乎、公众号、抖音、头条搜索【硬核项目经理】

B站ID:482780532

【PHP数据结构】插入类排序:简单插入、希尔排序的更多相关文章

  1. SDUT OJ 3403 数据结构实验之排序六:希尔排序

    数据结构实验之排序六:希尔排序 Time Limit: 1000 ms Memory Limit: 65536 KiB Submit Statistic Discuss Problem Descrip ...

  2. SDUT 3403 数据结构实验之排序六:希尔排序

    数据结构实验之排序六:希尔排序 Time Limit: 1000MS Memory Limit: 65536KB Submit Statistic Problem Description 我们已经学习 ...

  3. SDUT-3403_数据结构实验之排序六:希尔排序

    数据结构实验之排序六:希尔排序 Time Limit: 1000 ms Memory Limit: 65536 KiB Problem Description 我们已经学习了各种排序方法,知道在不同的 ...

  4. 数据结构与算法之PHP排序算法(希尔排序)

    一.基本思想 希尔排序算法是希尔排序,也称递减增量排序算法,是插入排序的一种更高效的改进版本. 该方法的基本思想是:先将整个待排元素序列分割成若干个子序列(由相隔某个“增量”的元素组成的)分别进行直接 ...

  5. 我的Java开发学习之旅------>Java经典排序算法之希尔排序

    一.希尔排序(Shell Sort) 希尔排序(Shell Sort)是一种插入排序算法,因D.L.Shell于1959年提出而得名. Shell排序又称作缩小增量排序. 二.希尔排序的基本思想 希尔 ...

  6. 算法相关——Java排序算法之希尔排序(五)

    个子块,即{3,5},{1,0},{5,2},{9,4},{6,12},将每个子块进行插入排序(即第i位与第i+5位进行比较交换),初步排序结果为{3,0,2,4,6,5,1,5,9,12}.希尔排序 ...

  7. 【DS】排序算法之希尔排序(Shell Sort)

    一.算法思想 希尔排序,也称递减增量排序算法,是插入排序的一种更高效的改进版本.希尔排序是非稳定排序算法.希尔排序是基于插入排序的以下两点性质而提出改进方法的:1)插入排序在对几乎已经排好序的数据操作 ...

  8. 算法与数据结构(十三) 冒泡排序、插入排序、希尔排序、选择排序(Swift3.0版)

    本篇博客中的代码实现依然采用Swift3.0来实现.在前几篇博客连续的介绍了关于查找的相关内容, 大约包括线性数据结构的顺序查找.折半查找.插值查找.Fibonacci查找,还包括数结构的二叉排序树以 ...

  9. 数据结构与算法系列——排序(4)_Shell希尔排序

    1. 工作原理(定义) 希尔排序,也称递减增量排序算法,是插入排序的一种更高效的改进版本.但希尔排序是非稳定排序算法. 希尔排序的基本思想是:先将整个待排序的记录序列分割成为若干子序列分别进行直接插入 ...

  10. 数据结构实验之排序六:希尔排序 (SDUT 3403)

    其实,感觉好像增量不同的冒泡,希尔排序概念以后补上. #include <bits/stdc++.h> using namespace std; int a[10005]; int b[1 ...

随机推荐

  1. 在Django中使用Channels功能

    前言:最近后台写游戏更新版本功能,简单就是前端发送更新请求,后端需要对很多台服务器进行更新和各种操作,本来想着实现不难,后来发现因为后端需要执行很长时间,前端返回报错,后端会执行完毕,但是前端先断开了 ...

  2. 【笔记】KNN之网格搜索与k近邻算法中更多超参数

    网格搜索与k近邻算法中更多超参数 网格搜索与k近邻算法中更多超参数 网络搜索 前笔记中使用的for循环进行的网格搜索的方式,我们可以发现不同的超参数之间是存在一种依赖关系的,像是p这个超参数,只有在 ...

  3. CF上部分树形DP练习题

    本次 5 道题均来自Codeforce 关于树形DP的算法讲解:Here 791D. Bear and Tree Jumps 如果小熊每次能跳跃的距离为1,那么问题变为求树上任意两点之间距离之和. 对 ...

  4. springboot:使用异步注解@Async的那些坑

    springboot:使用异步注解@Async的那些坑 一.引言 在java后端开发中经常会碰到处理多个任务的情况,比如一个方法中要调用多个请求,然后把多个请求的结果合并后统一返回,一般情况下调用其他 ...

  5. Asp.Net Core 导入Excel数据到Sqlite数据库并重新导出到Excel

    Asp.Net Core 导入Excel数据到Sqlite数据库并重新导出到Excel 在博文"在Asp.Net Core 使用 Sqlite 数据库"中创建了ASP.NET Co ...

  6. S3C2440—11.und异常

    文章目录 1 未定义指令 2 中断向量表 3 设置一个未定义指令 4 调用C函数 5 UND异常处理程序 6 汇编源码 7 注意点 lr与pc 保存现场 中断向量表的跳转 程序执行顺序 问题 1 未定 ...

  7. 利用VAR模型科学管理仓位,提升策略效率

    更多精彩内容,欢迎关注公众号:数量技术宅,也可添加技术宅个人微信号:sljsz01,与我交流. 期货行情瞬息万变,保证金体系决定了期货交易的杠杆属性.保证金放多了,资金利用率低,放少了,可能在大幅度的 ...

  8. 备战秋招之十大排序——O(n^2)级排序算法

    一.冒泡排序 冒泡排序是入门级的算法,但也有一些有趣的玩法.通常来说,冒泡排序有三种写法: 一边比较一边向后两两交换,将最大值 / 最小值冒泡到最后一位: 经过优化的写法:使用一个变量记录当前轮次的比 ...

  9. 数据结构解析-HashMap

    概要 HashMap在JDK1.8之前的实现方式 数组+链表,但是在JDK1.8后对HashMap进行了底层优化,改为了由 数组+链表+红黑树实现,主要的目的是提高查找效率. 如图所示: JDK版本 ...

  10. 【java虚拟机】jvm调优

    转自:https://www.cnblogs.com/starhu/p/6400348.html?utm_source=itdadao&utm_medium=referral 堆大小设置JVM ...