排序->内部排序->插入排序
1.直接插入排序
算法思想
每次将一个待排序的记录按照关键字大小插入已排好序的子序列,直到全部记录插入完成。
实现思路(顺序:非递减有序序列)
1. 记录当前需要排序元素的位置与大小(设位置为 i,大小为min )
2. 以 i 为分界线,其前为一个有序序列,其后为待排序元素
3. 将 min 与有序序列中的元素进行比较,直到该元素 <= min,否则将该元素后移。
4. 将 min 元素放入有序列表
5. 循环(i++),直到有序
实现代码
点击查看代码
/**
* 插入排序,最普通的实现方法(还有一种带哨兵的实现方法,就不在此实现了)
* @param arr
*/
public static void InsertSort(int[] arr){
for(int i = 1; i < arr.length; i++){
// 1. 当前需要排列的元素
int value = arr[i];
int j;
// 2. 以当前下标为分界线,前为有序,找到比他更小的元素
for(j = i-1; j >= 0; j--){
if(arr[j] <= value){
break ;
}
// 将比它大的元素后移
arr[j+1] = arr[j];
}
// 3. 将元素放在他应在的位置上
arr[j+1] = value;
}
}
2.折半插入排序
算法思想
在直接插入的基础上,可以寻找元素在有序表中的位置时使用折半查找的方法,能够减少时间复杂度。
算法思路
同上,但在进行 3 时使用折半查找的方法。
实现代码
点击查看代码
/**
* 折半插入排序
* 插入排序的一种优化,同样还有一种带哨兵的实现方法,不再次实现了。
* @param arr
*/
public static void InsertSortBin(int[] arr){
for(int i = 1; i < arr.length; i++){
int value = arr[i];
int left = 0;
int right = i-1;
int mid;
// 折半查找元素应该在的位置
while (left <= right){
mid = (left+right)/2;
if(arr[mid] <= value){
left = mid+1;
}else {
right = mid-1;
}
}
// 将该位置之后的元素后移
for(int j = i-1; j >= right+1; j--){
arr[j+1] = arr[j];
}
arr[right+1] = value;
}
}
3.希尔排序
算法思想
把待排序表分成若干个等长间隔的子表,对每个子表分别进行插入排序,当整个表中的元素都基本有序时,再对全体记录进行一次直接插入排序。
算法思路
1.取一个小于n的步长d1,将表中的元素分为d1组,所以距离为d1的元素放在一个组里;
2.组内进行直接插入排序;
3.取第二个步长d2 < d1,重复上述过程,直到dt = 1,再进行直接插入排序。
ps: 到目前为止,还没有得出最优的增量序列(步长序列),希尔提出的方法是每次折半,即d1 = n/2; d2 = d1/ 2,......直到最后增量等于1。
实现代码
点击查看代码
/**
* 希尔排序
* 第一个结点暂存元素,不参与排序
* @param arr
*/
public static void ShellSort(int[] arr){
for(int d = arr.length/2; d >= 1; d /= 2){
for(int i = d+1; i < arr.length; i++){
if(arr[i] < arr[i-d]){
arr[0] = arr[i];
int j;
for(j = i-d; j > 0 && arr[0] < arr[j]; j -= d){
arr[j+d] = arr[j];
}
arr[j+d] = arr[0];
}
}
}
}
4.其他结论
直接插入排序最坏情况下需要进行的比较次数为:n*(n-1)/2;
直接插入排序属于稳定排序,而希尔排序属于不稳定排序;
直接插入的三种排序方法时间复杂度都为O(n^2);