排序算法入门之「插入排序」

插入排序

借用《算法导论》里的例子,就是咱们打牌的时候,每新拿一张牌都会把它按顺序插入,这,其实就是插入排序。java

齐姐声明:虽然咱们用打牌的例子,可是可不能学胡适先生啊。git

对于数组来讲怎么作呢?github

有一个重要的思想,叫作挡板法,就是用挡板把数组分红两个区间:算法

  • 挡板左边:已排序
  • 挡板右边:未排序

那么排序分三步走:数组

  1. 最初挡板是在数组的最左边,index = 0 的位置,也就是保证了已排序区间里一个数都没有,或者也能够包含一个数啦;学习

  2. 核心思想就是:
    依次遍历未排序区间里的元素,在已排序区间里找到正确的位置插入;优化

  3. 重复这个过程,直到未排序区间为空。动画

举个例子:{5, 2, 1, 0}

第一步,挡板最初在这里:.net

第二步, 把 2 插入已排序区间的正确位置,变成:3d

重复这个步骤,把 1 排好:

最后把 0 排好:

那代码也很简单:

public void insertionSort(int[] input) {
	if (input.length <= 1) {
		return;
	}
	for(int i = 1; i < input.length; i++) {
		int tmp = input[i];
		int j = i - 1;
		while(j >= 0 && input[j] > tmp) {
			input[j+1] = input[j];
			j --;
		}
		input[j+1] = tmp;
	}
}

咱们来分析一下这个算法的时空复杂度。

时间复杂度

关于时间复杂度有两个要点

  • 是描述随着自变量的增加,所需时间的增加率;
  • 是渐近线复杂度,就是说
    • 不看系数
    • 只看最高阶项

那么咱们关心的 worst case 的状况就是:
若是数组是近乎倒序的,每次插入都要在数组的第一个位置插入,那么已排序区间内的全部的元素都要日后移动一位,这一步平均是 O(n),那么重复 n 次就是 O(n^2).

空间复杂度

重点是一个峰值的概念,并非累计使用的空间。 这里是 O(1) 没什么好说的。

引入一个概念:sorted in place,也就是原地排序

原地排序就是指空间复杂度为 O(1) 的算法,由于没有占用额外的空间,就是原地打转嘛。

其实 in-place 的思想并非只在排序算法里有,只不过排序算法是一个最广为人知的例子罢了。本质上就是一个节省使用空间的思想。

可是对于排序算法,只分析它的时空复杂度是不够的,还有另一个重要指标:

稳定性

这个是排序算法的一个重要指标,意思是元素之间的相对顺序是否保持了不变。

好比说:{5, 2, 2, 1, 0}

这个数组排序完成后这里面的两个 2 的相对顺序没有变,那么这个排序就是一个稳定排序。

那有同窗可能就想,顺序变了又有什么关系呢?

其实,在实际工做中咱们排序的对象不会只是一个数字,而是一个个的对象 (object),那么先按照对象的一个性质来排序,再按照另外一个性质来排序,那就不但愿原来的那个顺序被改变了。好像有点抽象,咱们举个例子。

好比在股票交易系统里,有买卖双方的报价,那是如何匹配的呢?

  • 先按照价格排序;
  • 在相等的价格中,按照出价的时间顺序来排序。

那么一搬来讲系统会维持一个按时间排序的价格序列,那么此时只须要用一个具备稳定性的排序算法,再按照价格大小来排序就行了。由于稳定性的排序算法能够保持大小相同的两个对象仍维持着原来的时间顺序。

那么插入排序是不是稳定性的排序呢?答案是确定的。由于在咱们插入新元素的时候是从后往前检查,并非像打牌的时候随便插一个位置不能保证相对顺序。

你们能够看下下面的动画 就很是清楚了~

优化

插入排序实际上是有很大的优化空间的,你能够搜一下“希尔排序”。

在刚开始学习的时候,深度当然重要,但由于广度不够,若是学的太深可能会很痛苦,一个知识点就无穷无尽的延展,这并非一个高效的学习方式。

因此若是时间有限,就要作好深度和广度的平衡:

  • 在经常使用常考的知识点上多花时间精力,追求深度;
  • 在一些拓展性的知识点上点到为止,先知道有这么回事就行。

保持 open minded 的心态,后期就会有质的提升。

若是你喜欢这篇文章,记得给我点赞留言哦~大家的支持和承认,就是我创做的最大动力,咱们下篇文章见!

我是小齐,纽约程序媛,终生学习者,天天晚上 9 点,云自习室里不见不散!

更多干货文章见个人 Github: https://github.com/xiaoqi6666/NYCSDE

相关文章
相关标签/搜索