小计
最近在找工作,越来越意识到算法的重要性,所以准备每天抽出一点时间来整理之前学过的算法,打卡第一天,希望可以坚持久一点。
直接插入排序
直接插入排序是从第二个元素开始,向前找合适的地方插入,插入以后,从插入后该元素所在位置到该元素插入前位置之间的数全部向后移动。
/**
* 直接插入排序
* @param a
*/
public static void insertSort(int a[]) {
//1.从第二个数据开始遍历,遍历到最后一个元素
for (int i = 1; i < a.length; i++) {
int temp = a[i];//保存这个需要移动位置的元素。
int j;
//2.向这个元素前面查找,直到找到一个值小于该元素,执行操作使找到小于该元素位置的数到该元素位置之间的数依次向后移动。
for (j = i - 1; j >= 0; j--) {
if (temp < a[j]) {
a[j + 1] = a[j];
} else {
break;
}
}
//将这个需要移动位置的元素设置到找到的合适的位置。
a[j + 1] = temp;
}
}
这是插入排序中最简单的算法,折半插入排序以及希尔排序都是建立在该排序基础之上的优化。
折半插入排序
折半插入排序是直接插入排序算法的优化,优化点在于他在寻找插入点时采用的查找方法不是一个个往前找,而是采用折半查找的方式,这样一般情况下会加快查找的速度。所以在代码实现时只是在查找位置方面有所变化。
/**
* 折半插入排序
*
* @param a
*/
public static void halfInsertSort(int a[]) {
// 用currInsertIndex代表当前要插入的数。
// 折半查找实现是确认low和high的下标,选择low和high中间的数,用mid表示,如果a[mid]>temp则使high=mid-1(递增查找),a[mid]<temp则使low=mid+1;否则是找到了。
// 1.从第二条数据开始遍历,进行插入排序
for (int i = 1; i < a.length; i++) {// 因为是插入排序,插入要选择currInsertIndex前面的某个位置,第一个数前没有数字,所以从第二个数开始
int temp = a[i];// 保存currInsertIndex,也就是要改变位置的元素。
int low = 0;
int high = i - 1;// 保存low的值和high的值,a[low]和a[high]以及之间的数代表着currInsertIndex的前面的所有数字,这些数字都有可能是currInsertIndex的插入点。
int mid;
// 2.进行插入点的查找,查找方式是折半查找
while (low <= high) {// 折半插入比直接插入优化的地方就在于他寻找插入点的位置是用折半查找来找的。
mid = (low + high) / 2;
if (a[mid] > temp) {
high = mid - 1;
} else {
low = mid + 1;
}
}
// 经过折半查找确认最后要插入的位置。
int j;
// 3.将currInsertIndex插入到指定位置,然后将插入点到currInsertIndex位置的数字后移。
for (j = i - 1; j >= low; j--) {
a[j + 1] = a[j];
}
a[j + 1] = temp;
}
}
希尔插入排序
希尔插入排序是将直接插入排序分组,经过不断改变分组的大小来使数据有序。一般分组是按照递归让数组长度除以2来进行分组的,然后分组后的每组数据进行直接插入排序,例如a[] = { 26, 53, 67, 48, 57, 13, 48, 32, 60, 50 };这时分组的集合是{5,2,1},通过三次分组和分组后的直接插入排序来使数组有序。
/**
* 希尔排序
*
* @param a
* @param d
*/
public static void ShellSort(int a[]) {
//1.数据分组,并设置每次分组的步长,这里用d来表示deep,即步长。
for (int d = a.length / 2; d >= 1; d /= 2) {
//2.从第一个步长后的数据开始,向后遍历,每个数据都向相同组内前面的数据进行直接插入排序。
for (int i = d; i < a.length; i++) {
//3.内部进行直接插入排序,只是这次在找前面的元素时步长为d。
int temp = a[i];
int j;
for (j = i - d; j >= 0; j -= d) {
if (a[j] > temp) {
a[j + d] = a[j];
} else {
break;
}
}
a[j + d] = temp;
}
}
}
插入类排序算法复杂度总结
插入类排序算法空间复杂度均为1
当数组基本有序时,使用直接插入排序比较好,希尔排序虽然能够提高速度,但是会降低稳定性。
排序算法 | 时间复杂度(最好) | 时间复杂度(最坏) | 时间复杂度(平均) | 空间复杂度(平均) | 稳定性 |
---|---|---|---|---|---|
插入排序 | O(n) | O(n^2) | O(n^2) | O(1) | 稳定 |
折半插入排序 | O(nlog2n) | O(n^2) | O(n^2) | O(1) | 稳定 |
希尔插入排序 | O(n^1.3) | O(1) | 不稳定 |