作者前言
大家好,我是阿濠,今篇内容跟大家分享的是排序算法之插入排序,很高兴分享到segmentfault与大家一起学习交流,初次见面请大家多多关照,一起学习进步.
一、插入排序的介绍
基本介绍
插入式排序属于内部排序法
,是对于欲排序的元素
以插入的方式
找寻该元素的适当位置
,以达到排序
的目的。.
排序思想
1.把n个待排序的元素
看成为一个有序表和一个无序表
2.开始时有序表
中只包含一个元素
,无序表
中包含有n-1个元素
3.排序过程
中每次从无序表中取出第一个元素
,把它的排序元素依次与有序表元素进行比较
4.将它插入
到有序表中的适当位置
,使之成为新的有序表
。
二、通过应用示例认识插入排序
由一群牛,考试成绩分别是: 100,34,119,1
使用插入排序法
将其排成一个从小到大的有序数列。
步骤思路:
1.把n个待排序的元素
看成为一个有序表和一个无序表
2.假设当前有序表
为待排序的第一个元素
,每次从无序表中取出第一个元素
,把它的排序元素依次与有序表元素进行比较
3.将它插入
到有序表中的适当位置
,使之成为新的有序表
。
//插入排序
//使用逐步推导的方式来讲解,便利理解
//第一轮{110,34,119,1}==>{34,101,119,1}
//当前有序表包含一个元素为110
//待插入数即是无序表:34,119,1 第一轮为34
int interValue=arr[1];//arr[1]=34
int interIndex= 1 - 1;//即arr[1]前一个位置
//给insertVal找到插入的位置
//说明
//1. insertIndex >= 0保证在给insertVal找插入位置,不越界
//2. insertVal < arr[insertIndex] 待插入的数, 还没有找到插入位置
//3. 从小到大排序,则若34小于110,则需要让位
while(interIndex >= 0 && interValue< arr[interIndex]){
arr[interIndex + 1]=arr[interIndex];
interIndex --;
}
//退出循环时,说明插入位置找到,insetIndex + 1
arr[interIndex +1 ] = interValue;
System.out.println("第一轮排序结果:");
System.out.println(Arrays.toString(arr));
运行结果如下:
第一轮排序结果:[34, 110, 119, 1]
//第二轮
interValue=arr[2];
interIndex=2 - 1;
while(interIndex >= 0 && interValue< arr[interIndex]){
arr[interIndex + 1]=arr[interIndex];
interIndex --;
}
//退出循环时,说明插入位置找到,insetIndex + 1
arr[interIndex +1 ] = interValue;
System.out.println("第二轮排序结果:");
System.out.println(Arrays.toString(arr));
运行结果如下:
第一轮排序结果:[34,110,119,1]
第二轮排序结果:[34,110,119,1]
//第三轮
interValue=arr[3];
interIndex=3 - 1;
while(interIndex >= 0 && interValue< arr[interIndex]){
arr[interIndex + 1]=arr[interIndex];
interIndex --;
}
//退出循环时,说明插入位置找到,insetIndex + 1
arr[interIndex +1 ] = interValue;
System.out.println("第三轮排序结果:");
System.out.println(Arrays.toString(arr));
运行结果如下:
第一轮排序结果:[34,110,119,1]
第二轮排序结果:[34,110,119,1]
第三轮排序结果:[1,34,110,119]
规律已经出来了,变化排序元素、比较交换
是相似
,改变的是从无序表中取出的元素
,即进行如下代码抽整:
//使用for循环来把代码进行简化
for(int i = 1; i<arr.length;i++){
int interValue=arr[i];
int interIndex= i - 1;
//给insertVal找到插入的位置
//说明
//1. insertIndex >= 0保证在给insertVal找插入位置,不越界
//2. insertVal < arr[insertIndex] 待插入的数, 还没有找到插入位置
//3. 从小到大排序,则若34小于110,则需要让位
while(interIndex >= 0 && interValue< arr[interIndex]){
arr[interIndex + 1]=arr[interIndex];
interIndex --;
}
//退出循环时,说明插入位置找到,insetIndex + 1
if(interIndex+1 !=i){
arr[interIndex +1 ] = interValue;
}
System.out.println("第"+(i)+"轮排序结果:");
System.out.println(Arrays.toString(arr));
}
//运行结果如下:
第1轮排序结果:[34, 110, 119, 1]
第2轮排序结果:[34, 110, 119, 1]
第3轮排序结果:[1, 34, 110, 119]
复杂度分析
1.最好的情况
,也就是要排序的表本身就是有序的
,此时只有数据比较
,没有数据移动
,时间复杂度为O(n)
。
2.最坏的情况
,即待排序的表是逆序的情况
,此时需要比较次数为:2+3+…+n=(n+2)(n-1)/2 次
,而记录移动的最大值也达到了(n+4)(n-1)/2次
.
3.如果排序记录是随机的
,那么根据概率相同的原则,平均比较和移动次数约为 (n^2) / 4次
,因此,得出直接插入排序发的时间复杂度为O(n^2)
。
可以看出同样的是时间复杂度
直接插入排序法
比冒泡和简单选择排序
的性能要好一些
。
直接插入排序是稳定的
,不会改变相同元素的相对顺序
。
三、 插入排序补充:二分插入排序
基本介绍
二分(折半)插入
(Binary insert sort) 排序是一种在直接插入排序
算法上进行小改动
的排序算法
。其与直接排序算法最大的区别在于查找插入位置时使用的是二分查找的方式
,在速度上有一定提升。
步骤思路:
一般来说,插入排序都采用in-place在数组上实现
。
1、从第一个元素开始
,该元素可以认为已经被排序
2、取出下一个元素
,在已经排序的元素序列中
二分查找到第一个比它大的数的位置
3、将新元素插入到该位置后
4、重复上述两步
假设插入数字:4
使用二分查找找到 mid
void BinInsertSort(int a[], int n)
{
int key, left, right, middle;
for (int i=1; i<n; i++)
{
key = a[i];//当前认为被排序的数
left = 0;
right = i-1;//当前认为被排序的数的右边
//将开始和结束的下标进行比较,如果不一致则遍历下一个
while (left<=right)
{
middle = (left+right)/2;//进行二分
//数组值比中间下标的数组值小,则在数组前一半中进行查找,即将end-1
if (a[middle]>key){
right = middle-1;
}
//如果而要计算的值比中间值大,则在数组后半段查找,即将start+1
else{
left = middle+1;
}
}
for(int j=i-1; j>=left; j--)
{
a[j+1] = a[j];
}
a[left] = key;
}
}
复杂度分析
1.稳定
2.空间代价:O(1)
3.时间代价:插入每个记录需要O(log i)比较
,最多移动i+1次
,最少2次
。最佳情况O(n log n),最差和平均情况O(n^2)
。
二分插入排序
是一种稳定的排序。
当n较大时
,总排序码比较次数比直接插入排序的最差情况好得多
,但比最好情况要差
,所元素初始序列已经按排序码接近有序时,直接插入排序比二分插入排序比较次数少
。
二分插入排序元素移动次数与直接插入排序相同
,依赖于元素初始序列
。