插入排序
public class Insertion {
private static boolean less(Comparable v,Comparable w){
return v.compareTo(w)<0;
}
private static void exch(Comparable[] a,int vIndex,int wIndex){
Comparable temp = a[vIndex];
a[vIndex] = a[wIndex];
a[wIndex] = temp;
}
private static void show(Comparable[] a){
for(int i=0,len=a.length;i<len;i++)
StdOut.print(a[i]+" ");
StdOut.println();
}
private static boolean isSorted(Comparable[] a){
for(int i=1,len=a.length;i<len;i++)
if(less(a[i],a[i-1])) return false;
return true;
}
public static void sort(Comparable[] a){
int N = a.length;
for(int i=1;i<N;i++)
for(int j=i;j>0&&less(a[j],a[j-1]);j--)
exch(a,j,j-1);
}
public static void main(String[] args){
String[] a = new In(args[0]).readAllStrings();
sort(a);
assert isSorted(a);
show(a);
}
}
实现策略
类似于打扑克牌拿牌的时候,手上的牌是已经有序的,现在拿上一张牌,对应数组中下一个元素,找到相应的位置进行插入,假设已经有序则,不需要进行交换
分析
与输入有关,假设已经有序则不会进行交换操作,整体运行时间在线性范围,适合处理那些存在一些顺序的排序。倒置是值排序后的元素与排序前的相对位置发生改变的情况,发生倒置的数量越少使用插入排序效率越高(遍历数组交换的数量越少)。最好情况下N-1次比较,0次交换,最差情况下(每次拿的新牌都是当前手中最小的,需要将其遍历整个数组放在最开始),需 要1+2+…N-1~N²/2次比较和N²/2次交换(第一次交换一个,第二次移动两个元素位置…),平均为N²/4次比较和N²/4次交换。
改进
可以不用每次都进行交换操作,改进策略可以先将较大的元素向后移动,找到真正的位置后进行插入操作,如下,一些注意在注释中给出。
public static void sortHalfExchange(Comparable[] a){
int N = a.length;
int exchanges = 0;
// 先找出最小值作为哨兵,以防超出范围
for(int i=N-1;i>0;i--){
if(less(a[i],a[i-1])){
exch(a,i,i-1);
exchanges++;
}
}
if(exchanges==0) return;
for(int i=2;i<N;i++){
Comparable v = a[i];// 当前插入的值
int j=i;
// 上一步保证了a[0]一定是最小的,不会出现超出ArrayIndexOutOfBoundsException的风险
while (less(v,a[j-1])){
a[j] = a[j-1];// 较大的值向后移动
j--;
}
a[j] = v;// 在插入位置插入值
}
}