插入排序
对于少量元素的排序,插入排序是个有效的算法。其排序方法如同我们平时排序扑克牌一般,现在我们开始排序扑克牌,目前我们的左手为空,桌面上的牌堆全部牌面向下,然后我们拿起牌堆顶部的一张牌,由于此时手里没牌,所以不用比较,直接放到手里,再拿起牌堆顶部的牌,跟手中的牌进行比较,大的牌放后面,再拿起一张,跟手中的牌从右向左进行比较,遇到小的牌便插入到此牌的后面,循环此操作直到所有的牌都在手中。如下图:
插入排序的伪代码:
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;
}
}
循环不变式与插入排序的正确性
循环不变式主要用来帮助我们理解算法的正确性。关于循环不变式,我们必须证明三条性质:
- 初始化:循环的第一次迭代之前,它为真。
- 保持:如果循环的某次迭代之前它为真,那么下次迭代之前它仍为真。
- 终止:在循环终止时,不变式为我们提供一个有用的性质,该性质有助于证明算法是正确的。
这有点类似于科学归纳法,证明某结论成立,需要证明一个基本情况和一个归纳步。分别对应第一、二条性质。
使用循环不变式来证明插入排序:
- 初始化:在第一次迭代之前,也就是当i=1时,子数组(a[0 … i])此时为a[0],只有一个元素,所以在第一次迭代之前子数组是有序的。
- 保持:在接下来的迭代之前,子数组已经是有序的,然后进行此次迭代,把key与子数组中的元素进行比较,如果子数组的元素比该key大,则子数组中的元素右移,直到找到key适合的位置,将其插入到该位置,此时子数组有序。那么下次迭代进行之前子数组也是有序的。
- 终止:当i=a.length时,循环终止。根据前面的性质,此时子数组是有序的,且此时子数组为a[0 … n-1],也就是子数组等于整个数组,所以整个数组已排序,因此算法正确。
练习
答:略。
答:略
答:
for (int i = 0; i < a.length; i++) {
if (a[i] == v) {
return i;
}
}
return NIL;
把已扫描过的数组元素称为b[]。
- 初始化:当第一次迭代之前,也就是i=0时,此时在b[]中没有元素,也就是此时v不在b[]中,此时结果为NIL;
- 保持:在接下的迭代之前,结果为NIL,然后进行此次迭代,如果该循环没有终止,说明v不在b[]中,那么结果依然为NIL,那么下次迭代之前,结果仍为NIL。
- 终止:当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;