『荐读』算法一看就懂之「 插入排序 」

Posted WHITer

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了『荐读』算法一看就懂之「 插入排序 」相关的知识,希望对你有一定的参考价值。

作者 | 奎哥  


上一篇文章咱们已经聊过了,今天咱们来看一看「 插入排序 」。「 插入排序 」与「 冒泡排序 」一样都属于时间复杂O(n*n)的排序算法,并且也都是基于元素之间比较的方式来完成排序的。不过「 插入排序 」比「 冒泡排序 」在实际应用中更广泛一些,因为它的执行效率更高一点。

一、「 插入排序 」是什么?

插入排序 是一种最简单的排序算法,它的思路是将一组待排序的数据,分成2段,一段是“已经排序”了的数据,另一段是“未排序”的数据。只要每次都从“未排序”的数据中取出一个元素,将这个元素插入到“已经排序”数据中的正确的位置(可能会涉及到原有元素的移动),那么插入后,“已经排序”区段中的数据依然是有序的,只要这样不停的循环,直到所有的“未排序”的数据都已取完,则整个排序完成。

『荐读』算法一看就懂之「 插入排序 」

这个原理很像大家平时娱乐时打的扑克牌,在每一局开始的取牌阶段,每取一张牌,就将这张牌插入到手中那些已排好的牌中的正确位置,直到所有的牌取完。

下面用一个整数数组做一个示例:

初始状态 8 3 6 2 7 9 移动次数
第一遍 3 8 6 2 7 9 1
第二遍 3 6 8 2 7 9 1
第三遍 2 3 6 8 7 9 3
第四遍 2 3 6 7 8 9 1
第五遍 2 3 6 7 8 9 0

上述示例中,初始数组是 8,3,6,2,7,9,在初始状态时,我们将将数组分为2个段,第一个元素8当做是“已经排序”了的区段,后面所有的元素是作为“未排序”的区段。然后我们开始循环,依次拿出后面“未排序”的区段中的元素与“已经排序”了的区段进行比较,并找到合适的位置插入。

  1. 第一遍循环时,从“未排序”的区段中拿出元素3,它比“已经排序”段中的元素8小,因此需要将元素8向后移动一位,留出空位让元素3插入。这次只需要移动一个元素,表中也标注了移动次数为1次。

  2. 第二遍循环时,从“未排序”的区段中拿出元素6,它比“已经排序”段中的元素3大、比元素8小,因此元素3不动,只需要将元素8向后移动一位,留出空位让元素6插入,这次移动次数也为1次。

  3. 第三遍循环时,从“未排序”的区段中拿出元素2,它比“已经排序”段中的元素2小,因此需要将元素3、元素6、元素8均向后移动一位,留出空位让元素2插入,这次移动次数为3次。

  4. 第四遍循环时,从“未排序”的区段中拿出元素7,它比“已经排序”段中的元素6大、比元素8小,因此需要将元素8向后移动一位,留出空位让元素7插入,这次移动次数为1次。

  5. 第五遍循环时,从“未排序”的区段中拿出元素9,它比“已经排序”段中的元素8大,因此“已经排序”的区段无需移动,直接在最后的位置将元素9插入,这次移动次数为0次。

说再多都不与看代码来得直接,下面我们来看一个插入排序的代码:

算法题:对数组arr进行从小到大的排序,假设数组arr不为空,arr的长度为n思路:采用插入排序方法
public void inertSort(int[] arr){ int i,j; int n = arr.length; for(i=1; i<n; i++){ //位置0是“已经排序”区段,因此从位置1开始,依次取出“未排序”区段的元素 int needSort = arr[i]; //在“已经排序”区段中,从后往前循环,对比needSort for(j=i-1; j>=0; j--){ //如果needSort小于arr[j],则说明需要将arr[j]往后移动一位,以便留出空位 if(val < arr[j]){ arr[j+1] = arr[j]; }else{ break; } } //将元素放入到正确的位置 arr[j+1] = needSort; }}

二、「 插入排序 」的性能怎么样?

我们按照之前文章中讲到的排序算法评估方法来对「 插入排序 」进行一下性能评估:

  • 时间复杂度:

    插入排序原理就是在两层嵌套循环里进行对比,所以简单来讲,其一般情况下的时间复杂度就是O(n*n)了。但如果仔细去分析的话,就得看具体的数据情况,如果待排序的数据本身就是有序的,那么我们只需要做外层循环就可以(因为内层循环每次都是一开始就判定不成立而立即终止),此时就是最好的情况,其时间复杂度为:O(n),但如果待排序的数据全部都是逆序的,那我们在每次内循环中都需要做大量的移动数据的操作,其最坏情况下,时间复杂度就是:O(n*n)了。

  • 空间复杂度:

    插入排序完全就是移动数据,只需要一个辅助空间用来存储待比较的元素,并没有消耗更多的额外空间。因此其空间复杂度是O(1)。

  • 排序稳定性:

    在本系列的最开始介绍过了排序算法稳定性的定义,这里不重复介绍了。对于插入排序,在做元素对比的时候,如果元素大小相同,则可以将后面的元素插入到当前元素的后面,这样就可以保证它们原有的相对位置不变了,因此它是排序稳定的。

  • 算法复杂性:

    插入排序的算法无论是其设计思路上,还是代码的编写上都不复杂,其算法复杂性是比较简单的,可以说插入排序是最简单的排序算法之一了。

以上,就是对数据结构中「 插入排序 」的一些思考,您有什么疑问吗?

码字不易啊,喜欢的话不妨转发朋友,或点击文章右下角的“在看”吧。

以上是关于『荐读』算法一看就懂之「 插入排序 」的主要内容,如果未能解决你的问题,请参考以下文章

算法一看就懂之「 选择排序 」

算法一看就懂之「 冒泡排序 」

算法一看就懂之「 递归 」

一看就懂之:RPC 反向代理

一看就懂之 webpack 高级配置与优化

一看就懂的冒泡排序