大家好,我是烤鸭:
今天分享一下基础排序算法之直接插入排序。
1. 直接插入排序:
原理:假设前面的数为有序数列,然后有序数列与无序数列的每个数比较,我们可以从右向左比较
思路:从第2个数开始,和1比较。这样前2个有序。
第3个和前2个比较,这样前3个有序。(如果是最小的,则第3个元素处在第1个位置,后面的元素后移1。)
第4个和前3个比较,同上。
直到第 n 个元素 和 前 n-1 个比较。
代码实现:
/**
* 直接插入排序
* directInsertSort
*
* @param array 时间复杂度,O的n^2
* 直接插入排序就是我们假设前面的数为有序数列,然后有序数列与无序数列的每个数比较,我们可以从右向左比较
* 当 array[i]<=array[j]=
*/
public void directInsertSort(int[] array) {
long nowTime = System.currentTimeMillis();
int tem = 0;
for (int i = 1; i < array.length; i++) {
int j = i - 1;
tem = array[i];
for (; j >= 0 && array[j] > tem; j--) {
array[j + 1] = array[j];//将大于array[i]的数整体后移一单位
}
array[j + 1] = tem;
}
System.out.println("直接插入排序,花费时间(s):" + ((System.currentTimeMillis() - nowTime) / 1000.0) + "s");
}
2. 折半插入排序(优化):
思路:
其实和直接插入排序是类似的,只是在遍历元素的时候采用的是二分法,直插采用的是顺序遍历。
取 temp 作为当前元素,
begin从0开始,end到数组最后一个元素。
如果temp < 中间值,begin从中间值+1继续,否则 end 变为 end - 1 继续,
begin 到 i 整体后移。
如图:(图片来源 http://www.cnblogs.com/chengxiao/p/6103002.html)
代码实现:
/**
* 折半插入排序
* @param source
* halfInsertSort
*
* @param source 时间复杂度,O的n^2
* 折半插入排序算法是一种稳定的排序算法,比直接插入算法明显减少了关键字之间比较的次数,
* 因此速度比直接插入排序算法快,但记录移动的次数没有变,所以折半插入排序算法的时间复杂度仍然为O(n^2),
* 与直接插入排序算法相同
*/
public static void halfInsertSort(int[] source) {
long nowTime = System.currentTimeMillis();
int size = source.length;
for (int i = 1; i < size; i++) {
// 拿出来
int temp = source[i];
int begin = 0; // 标记排好序的数组的头部
int end = i - 1; // 标记排好序数组的尾部
// 只要头部一直小于尾部,说明temp还在2个标记范围内
while (begin <= end) {
// 取2个标记的中间数据的值
int mid = (begin + end) / 2;
// 比较,若比中间值大,则范围缩小一半
if (temp > source[mid]) {
begin = mid + 1;
// 否则,范围也是缩小一半
} else {
end = mid - 1;
}
// 循环结束时,end<begin,即i应该插入到begin所在的索引
}
// 从begin到i,集体后移
for (int j = i; j > begin; j--) {
source[j] = source[j - 1];
}
// 插入i
source[begin] = temp;
}
System.out.println("折半插入排序,花费时间(s):" + ((System.currentTimeMillis() - nowTime) / 1000.0) + "s");
}
3. shell排序
思路:
Shell排序也是对直接插入排序的改进。它实质上是一种分组插入方法。
下面希尔排序的步长选择都是从n/2开始,每次再减半,直到最后为1。
时间复杂度 : O(nlog2^n)
/**
* 希尔排序
* 针对直接插入排序的下效率问题,有人对次进行了改进与升级,这就是现在的希尔排序。
* 希尔排序,也称递减增量排序算法,是插入排序的一种更高效的改进版本。希尔排序是非稳定排序算法。
* 首先确定分的组数。
* 然后对组中元素进行插入排序。
* 然后将length/2,重复1,2步,直到length=0为止。
* @param arr
*/
public void shellSort(int [] arr){
long nowTime = System.currentTimeMillis();
int len=arr.length;//单独把数组长度拿出来,提高效率
while(len!=0){
len=len/2;
for(int i=0;i<len;i++){//分组
for(int j=i+len;j<arr.length;j+=len){//元素从第二个开始
int k=j-len;//k为有序序列最后一位的位数
int temp=arr[j];//要插入的元素
/*for(;k>=0&&temp<arr[k];k-=len){
arr[k+len]=arr[k];
}*/
while(k>=0&&temp<arr[k]){//从后往前遍历
arr[k+len]=arr[k];
k-=len;//向后移动len位
}
arr[k+len]=temp;
}
}
}
System.out.println("希尔排序,花费时间(s):" + ((System.currentTimeMillis() - nowTime) / 1000.0) + "s");
}
耗时对比:
10W 条随机 数据 运行如图:
可以看出希尔排序时间明显(比直插排序和折半排序)缩短。折半排序和直插排序时间差不多。
50W 条随机 数据 运行如图:
可以看出希尔排序时间明显(比直插排序和折半排序)缩短。折半排序和直插排序时间差不多。
100W 条随机 数据 运行如图:
可以看出希尔排序时间明显(比直插排序和折半排序)缩短。折半排序比直插排序耗时更多。
总结:
直接插入排序写法比较简单,平均时间复杂度为:O(n^2) 。
折半插入排序,平均时间复杂度为:O(n^2) 。
希尔排序,平均时间复杂度为:O(nlog2^n) 。
各种排序方法比较:
更多排序算法:
冒泡排序 : https://blog.csdn.net/Angry_Mills/article/details/81057900