一.概念
基本操作:
插入位置:a[i]即将要插入的位置j,有可能是中间、开始、结尾。
插入排序种类:按寻找插入位置划分
二.直接插入排序
代码1:
两层循环,第一层遍历所有元素将其排序,第二层寻找插入位置和移动元素
/**
* 普通插入, 问题每次循环需要对比两个条件 j>0 x<a[j]
*/
public static void normal(){
int[] a=new int[]{9,7,8,6,5,4,3,2,1};
int x;
int j;
for (int i=1;i<a.length;i++){
x=a[i];
//边寻找插入位置,边将有序数组后移
for (j=i-1;j>=0&&x<a[j];j--){
a[j+1] = a[j];
}
//j+1为应该插入的位置,循环退出时 j-1
a[j+1] = x ;
}
for (int i=0;i<a.length;i++){
System.out.print(a[i]);
}
}
代码2:哨兵
/**
* 哨兵直接插入, 每次循环对比一个条件
*/
public static void sentinel(){
//数组的第一个元素做哨兵,不参与排序
int[] a=new int[]{0,9,7,8,6,5,4,3,2,1};
int j;
for (int i=2;i<a.length;i++){
//如果当前遍历符合顺序,就不需要再往前比对
if (a[i]>a[i-1])
continue;
a[0] = a[i];
for (j=i-1;a[0]<a[j];j--){
a[j+1] = a[j];
}
a[j+1] = a[0];
}
for (int i=1;i<a.length;i++){
System.out.print(a[i]);
}
}
效率分析
最好情况,数组本身有序,只需要逐次对比,不需要移动。
最坏的情况,数组本身逆序, 则第2个元素需要对比1次,第3个对比2次,第4个对比3次,对比总数为 1+2+...(n-1)。
每次对比,都要把前面元素往后移动,并且哨兵每次都移动。
三.折半插入
直接插入在寻找插入位置时,是将前面的有序数组从后往前遍历,折半插入就是用二分法寻找插入位置。找到插入位置后,就插入并后移数组,和直接插入操作一样。
private static void half2() {
// int[] a = new int[]{0, 1, 2, 3};
int[] a = new int[]{0, 9, 7, 8, 6, 5, 4, 3, 2, 1};
for (int i = 2; i < a.length; i++) {
a[0] = a[i];//赋值哨兵
int low = 1;
int high = i - 1;//有序队列的尾结点索引
while (low < high) {
int mid = (low + high) / 2;
if (a[0] < a[mid]) {
if (mid > 1) { //mid有时会出现1 的情况,比如 low=1 high=2,如果不判断会导致 high=0,把哨兵也排序
high = mid - 1;
} else {
high = 1;
}
} else {
low = mid + 1;
}
}
//high就是插入位置,为什么是high? 不管最初有多少元素,折半查找最后只会剩下两个相邻的元素,如 3 4,此时mid等于较小的那个数,
//如果a[mid]<a[0], high = 4,low =4,high就是插入位置
//如果a[mid] >=a[0],high = 3,low=3,high也是插入位置,最终high 和low会相等,都是插入位置
if (a[0] < a[high]) { //如果原数组本身顺序,如{0, 1, 2, 3},会变成 2 1 3 ,再判断一次是否需要排序
for (int j = i; j > high; j--) {
a[j] = a[j - 1];
}
a[high] = a[0];
}
}
for (int k = 1; k < a.length; k++) {
System.out.print(a[k]);
}
}
四.希尔排序
希尔排序由直接插入排序思想而来:直接插入,在原数组基本有序时,效率高,在待排序的元素少时,效率高。希尔排序想比较一次,可以移动一大步
private static void studyShell(){
int arr[] = new int[]{1,8,6,9,9,4,7,3,8,9,3,2,7};
//step不管最初多少,最后都会是1,保证都会排一遍
for (int step=arr.length/2;step>0;step/=2){
//首先记住,插入排序 是将一个数 在一个有序数组中,找到合适位置插入, 先有有序数组 再去插入
//假设step =3 执性i=3时还没有有序数组,但执行i=6时, 0 和 3是有序的, 这个有序数组是逐渐得到的,第一个step还没有有序数组,第二个就有了,再往后就是正常插入排序
//i=3 6 9会将同组排序,i递增,i= 4 7 10也会将同组排序,这样就按照希尔分组都排了
for(int i=step;i<arr.length;i++){
int x = arr[i];
int j = i-step;
while (j>=0&&x<arr[j]){
arr[j+step] = arr[j];
j-=step;
}
arr[j+step] = x;
}
}
for (int i=0;i<arr.length;i++){
System.out.print(arr[i]);
}
}
不宜在链式存储,无法直接根据索引获取值。