插入排序
在jdk1.8源码中,Arrays.sort方法中运用了普通插入排序与成对插入排序,这里主要讲解这两种算法思想
普通插入排序
普通插入排序的思想是,每一步将一个待排序的记录,将其顺序大小插入到前面已经排序的序列的合适位置。从后向前找合适位置
下面是Arrays.sort中的普通插入排序,边界是left到right
for (int i = left, j = i; i < right; j = ++i) {
int ai = a[i + 1];
while (ai < a[j]) {
a[j + 1] = a[j];
if (j-- == left) {
break;
}
}
a[j + 1] = ai;
}
可能你一下子看不出来是普通插入排序,因为你可能习惯了以下的形式
for (int i=left+1;i<=right;i++) {
int temp = a[i];
int j=i;
while (j>left&&a[j-1]>temp) {
a[j]=a[j-1];
j--;
}
a[j]=temp;
}
源码中的插入排序稍微做了一点变换,将j>left判断放置进while内部
成对插入排序
在排序源码中,会发现当设置的left不是边界时,可能会进入成对插入排序
以下为源码的成对插入排序
//优化,跳过升序部分
do {
if (left >= right) {
return;
}
} while (a[++left] >= a[left - 1]);
//成对插入排序
for (int k = left; ++left <= right; k = ++left) {
int a1 = a[k], a2 = a[left];
if (a1 < a2) {
a2 = a1; a1 = a[left];
}
while (a1 < a[--k]) {
a[k + 2] = a[k];
}
a[++k + 1] = a1;
while (a2 < a[--k]) {
a[k + 1] = a[k];
}
a[k + 1] = a2;
}
int last = a[right];
while (last < a[--right]) {
a[right + 1] = a[right];
}
a[right + 1] = last;
成对插入排序的思想是:
(1)将要插入的数据,第一个值赋值a1,第二个值赋值a2,
(2)然后判断a1与a2的大小,使a1要大于a2
(3)接下来,首先是插入大的数值a1,将a1与k之前的数字一一比较,直到数值小于a1为止,把a1插入到合适的位置,注意:这里的相隔距离为2
(4)接下来,插入小的数值a2,将a2与此时k之前的数字一一比较,直到数值小于a2为止,将a2插入到合适的位置,注意:这里的相隔距离为1
(5)最后把最后一个没有遍历到的数据插入到合适位置
这里的a2与a1的间隔的差别是因为,a1>a2,大于a1的一定大于a2
成对插入排序的例子分析
这里通过一个简单的数组来说明运行的过程
a[4,7,5,3,6,2,1]
通过优化部分后,left=2,指向5的部分,进入成对插入排序for循环
分析第一次进入
for (int k = left; ++left <= right; k = ++left) { //k=2,left=3
int a1 = a[k], a2 = a[left]; //a1=5,a2=3
if (a1 < a2) {
a2 = a1; a1 = a[left];
}
while (a1 < a[--k]) { //k=1 a[3]=a[1]=7 此时数组是 [4,7,5,7,6,2,1],出循环时k=0
a[k + 2] = a[k];
}
a[++k + 1] = a1; //k=1 a[2]=a1=5,此时数组是 [4,7,5,7,6,2,1]
while (a2 < a[--k]) { //k=0 a[1]=a[0]=4 此时数组是 [4,4,5,7,6,2,1],出循环时k=-1
a[k + 1] = a[k];
}
a[k + 1] = a2; //a[0]=3,此时数组是[3,4,5,7,6,2,1],发现这一轮有两个数字插入排序好了,一个是5,一个是3
}
后面的每一轮跟此轮类似