插入排序

对于少量元素的排序,插入排序是个有效的算法。其排序方法如同我们平时排序扑克牌一般,现在我们开始排序扑克牌,目前我们的左手为空,桌面上的牌堆全部牌面向下,然后我们拿起牌堆顶部的一张牌,由于此时手里没牌,所以不用比较,直接放到手里,再拿起牌堆顶部的牌,跟手中的牌进行比较,大的牌放后面,再拿起一张,跟手中的牌从右向左进行比较,遇到小的牌便插入到此牌的后面,循环此操作直到所有的牌都在手中。如下图:

第二章、算法基础 -- 插入排序

插入排序的伪代码:
第二章、算法基础 -- 插入排序

java实现:

public static void sort(int[] a) {
	for (int i = 1; i < a.length; i++) {
		int key = a[i];
		int j = i - 1;
		for (; j >= 0 && key < a[j]; j--) {
			a[j + 1] = a[j];
		}
		a[j + 1] = key;
	}
}

循环不变式与插入排序的正确性

循环不变式主要用来帮助我们理解算法的正确性。关于循环不变式,我们必须证明三条性质:

  1. 初始化:循环的第一次迭代之前,它为真。
  2. 保持:如果循环的某次迭代之前它为真,那么下次迭代之前它仍为真。
  3. 终止:在循环终止时,不变式为我们提供一个有用的性质,该性质有助于证明算法是正确的。

这有点类似于科学归纳法,证明某结论成立,需要证明一个基本情况和一个归纳步。分别对应第一、二条性质。

使用循环不变式来证明插入排序:

  1. 初始化:在第一次迭代之前,也就是当i=1时,子数组(a[0 … i])此时为a[0],只有一个元素,所以在第一次迭代之前子数组是有序的。
  2. 保持:在接下来的迭代之前,子数组已经是有序的,然后进行此次迭代,把key与子数组中的元素进行比较,如果子数组的元素比该key大,则子数组中的元素右移,直到找到key适合的位置,将其插入到该位置,此时子数组有序。那么下次迭代进行之前子数组也是有序的。
  3. 终止:当i=a.length时,循环终止。根据前面的性质,此时子数组是有序的,且此时子数组为a[0 … n-1],也就是子数组等于整个数组,所以整个数组已排序,因此算法正确。

练习

第二章、算法基础 -- 插入排序
答:略。
第二章、算法基础 -- 插入排序
答:略
第二章、算法基础 -- 插入排序
答:

for (int i = 0; i < a.length; i++) {
	if (a[i] == v) {
		return i;
	}
}
return NIL;

把已扫描过的数组元素称为b[]。

  1. 初始化:当第一次迭代之前,也就是i=0时,此时在b[]中没有元素,也就是此时v不在b[]中,此时结果为NIL;
  2. 保持:在接下的迭代之前,结果为NIL,然后进行此次迭代,如果该循环没有终止,说明v不在b[]中,那么结果依然为NIL,那么下次迭代之前,结果仍为NIL。
  3. 终止:当a[i]=v或者i=a.length时,循环终止。当a[i]=v时,那么已经查找到数组中等于v的元素,此时i为所查找元素的下标,算法正确;当i=a.length时,那么v没有在b[]中,此时b[]=a[],所以v没有在数组中,结果为NIL,算法正确。

第二章、算法基础 -- 插入排序
答:

输入:两个n位序列A、B,序列的每一个元素是n位二进制数对应的位。
输出:一个(n+1)位序列C,序列的每一个元素是这两个二进制数的和对应的位。

	int[] c = new int[a.length + 1];
	int carry = 0;
	for (int i = 0; i < a.length; i++) {
		int result = a[i] + b[i] + carry;
		c[i] = result % 2;
		if (result >= 2) {
			carry = 1;
		} else {
			carry = 0;
		}
	}
	c[a.length] = carry;
	return c;

相关文章: