排序算法可以说是算法中最基础的一部分知识,插入排序又可以称之为是排序算法中的基础,所以理解插入排序的算法原理对于学习算法也是很有帮助的。
算法导论中对于插入算法是这样描述的:插入排序的工作方式像许多人排序一手扑克牌,开始时我们的左手为空并且桌子上的牌面向下(需要排序的数组),然后,我们每次从桌子上拿走一张牌,从右到左将它与已在左手中的每张牌进行比较,最后将它插入左手中正确的位置,所以我们拿在手中的牌总是有序的。
《算法导论》在插入排序这个内容中提到了一个概念:循环不变式。我对于循环不变式的理解是:在插入排序中,循环不变式可以看做为一个遵循排序规则的、在排序过程中不断改变的、属于排序数组的子集。
以数组A={5,2,4,6,1,3}为例,插入排序开始时:循环不变式为{5};完成元素6的排序时:循环不变式为{2,4,5,6};完成排序之后:循环不变式为{1,2,3,4,5,6},即排序的最终结果
以上述A={5,2,4,6,1,3}为例,解释插入排序原理:
初始化状态如下表所示:此时的循环不变式为A[0]
索引 | 0 | 1 | 2 | 3 | 4 | 5 |
值 | 5 | 2 | 4 | 6 | 1 | 3 |
第一次排序:从索引为1的位置,即2视为关键值,开始排序,此时循环不变式为:{5},即A[0],将索引视为i,也可以说是A[0...i-1]。插入排序就是为关键值在循环不变式中从右向左寻找合适的位置,如果循环不变式中有数比关键值大,则该数向右移动一位。
索引 | 0 | 1 | 2 | 3 | 4 | 5 |
值 | 2 | 5 | 4 | 6 | 1 | 3 |
第二次排序:从索引为2的位置,即4视为关键值,开始排序,此时循环不变式为:{2,5},即A[0...1],将索引视为i,也可以说是A[0...i-1]。将关键值与循环不变式中的值进行比较。
索引 | 0 | 1 | 2 | 3 | 4 | 5 |
值 | 2 | 4 | 5 | 6 | 1 | 3 |
第三次排序:从索引为3的位置,即6视为关键值,开始排序,此时循环不变式为:{2,4,5},即A[0...2],将索引视为i,也可以说是A[0...i-1]。将关键值与循环不变式中的值进行比较。
索引 | 0 | 1 | 2 | 3 | 4 | 5 |
值 | 2 | 4 | 5 | 6 | 1 | 3 |
第四次排序:从索引为4的位置,即1视为关键值,开始排序,此时循环不变式为:{2,4,5,6},即A[0...3],将索引视为i,也可以说是A[0...i-1]。将关键值与循环不变式中的值进行比较。 此时循环不变式中的值都比1要大。
索引 | 0 | 1 | 2 | 3 | 4 | 5 |
值 | 1 | 2 | 4 | 5 | 6 | 3 |
第五次排序:从索引为5的位置,即3视为关键值,开始排序,此时循环不变式为:{1,2,4,5,6},即A[0...4],将索引视为i,也可以说是A[0...i-1]。将关键值与循环不变式中的值进行比较。
索引 | 0 | 1 | 2 | 3 | 4 | 5 |
值 | 1 | 2 | 3 | 4 | 5 | 6 |
此时排序完成。
以下是具体的Java代码实现:
/**
* 插入排序
*/
public class Insert_sort {
public static void main(String[] args) {
int[] arr1 = {5, 2, 4, 6, 1, 3};
// 插入排序
insertion_sort(arr1);
}
public static void insertion_sort(int[] arr1) {
System.out.println("初始值:");
for (int i : arr1) {
System.out.print(i + " ");
}
System.out.println();
// for循环从1号元素开始,是将0号元素作为了初始循环不变式
for (int i = 1; i < arr1.length; i++){
int key = arr1[i];
// i号元素,即关键值与循环不变式中的元素进行
int j = i -1;
// 循环不变式中的元素都是已经排好序的
// 将循环不变式中所有比关键值大的数向右移动一位
while(j >= 0 && arr1[j] > key){
arr1[j+1] = arr1[j];
j -= 1;
}
// 移动好之后,循环不变式中空余的位置就是关键值,即原来的i号元素的位置
arr3[j+1] = key;
}
System.out.println("排序后结果:");
for (int i : arr1) {
System.out.print(i + " ");
}
}
}