一、基本思想
在一个已排好序的记录子集的基础上,每一步将下一个待排序的记录有序插入到已排好序的记录子集中,直到将所有待排记录全部插入为止。例如:打扑克牌。
二、直接插入排序(升序)
思想:在有序部分,查找合适的位置。
查找方法:遍历查找(遍历又分为从后往前和从前往后)和二分查找。
适用场景:在待排序的关键字序列基本有序且关键字个数n较少时,其算法性能最佳。
1.遍历查找
将第i个记录插入到前面i-1个已排好序的记录中。 通常我们会在i-1个排好序的序列中从后往前遍历,直到找到比它小的数,并将第i个数插入到该数后面。
public class InsertSort {
//直接插入排序(升序)
//思路:遍历每一个元素,将下标为i的元素插入到前面有序的数组中。插入的位置需要从后往前遍历,直到找到比它小的数,插入到比它小的数的后面
//移动的元素下标范围:[j+1,i-1]-->[j,i] 从后往前搬移 优化:边遍历边搬移
//有序:[0,i) 无序:[i,array.length-1]
private static void insertSort(int[] array){
for(int i=0;i<array.length;i++){
int key=array[i];//记录
int j=i-1;
for(;j>=0;j--){
if(key<array[j]){
array[j+1]=array[j];
}else{
break;
}
}
array[j+1]=key;//插入array[i]
}
}
public static void main(String[] args) {
int[] array=new int[]{34,23,49,3,45,100,324};
insertSort(array);
for(int item:array){
System.out.print(item+" ");
}
}
}
2.二分查找
public class InsertSort {
//左闭右开形式查找
private static void binInsertSort(int[] array){
//遍历每一个数,在已排好序的序列中[0,i]找到合适的插入位置
for(int i=0;i<array.length;i++){
int key=array[i];
int left=0;
int right=i;
while(left<right){//==时,区间没有一个数据
int mid=left+(right-left)/2;
if(array[mid]==key){
left=mid+1;
}else if(array[mid]>key){
right=mid;//右开
}else{
left=mid+1;
}
}
//left就是要插入的位置
//搬移数据[left,i)
for(int p=i;p>left;p--){
array[p]=array[p-1];
}
array[left]=key;
}
}
//左闭右闭形式查找
private static void binInsertSort2(int[] array){
for(int i=0;i<array.length;i++){
int key=array[i];
int left=0;
int right=i-1;//左闭右闭
while(left<=right){
int mid=left+(right-left)/2;
if(array[mid]==key){
left=left+1;
}else if(array[mid]>key){
right=mid-1;
}else{
left=left+1;
}
}
//left就是要插入的位置 将[left,i-1]的数据搬移到[left+1,i]的位置上
for(int p=i;p>left;p--){
array[p]=array[p-1];
}
array[left]=key;
}
}
public static void main(String[] args) {
int[] array=new int[]{34,23,49,3,45,100,324};
binInsertSort(array);
for(int item:array){
System.out.print(item+" ");
}
System.out.println();
binInsertSort2(array);
for(int item:array){
System.out.print(item+" ");
}
}
}
3.时间复杂度、空间复杂度和稳定性分析
直接插入排序(遍历查找):
时间复杂度:最好:O(n) 最坏:O(n^2) 平均:O(n^2)
空间复杂度:O(1)
稳定性:稳定
直接插入排序(二分查找):
时间复杂度: 最好:O(n) 最坏:O(n^2) 平均:O(n*log(n))
空间复杂度:O(1)
稳定性:稳定
二、希尔排序
1.思想:利用直接插入排序的最佳性质,首先,将待排序的关键字序列分成若干个较小的子序列,对子序列进行直接插入排序,使整个待排序序列排好序(预排序)。最后,再对整体进行直接插入排序。
如何分组?一般令gap=length,令gap=(gap/3)+1或者gap=gap/2.
public class InsertSort {
//希尔排序
private static void shellSort(int[] array){
int gap=array.length;
while(gap>0){
for(int i=0;i<gap;i++) {
//分组
shellinsertSort(array, i,gap);
}
gap=gap/2;
}
}
private static void shellinsertSort(int[] array,int begin,int gap){
for(int i=begin;i<array.length;i+=gap){
//遍历每个已排好序的数[begin,begin+gap,...i)
int key=array[i];
int j=i-gap;
for(;j>=begin;j-=gap){
if(key<array[j]){
array[j+gap]=array[j];
}else{
break;
}
}
array[j+gap]=key;
}
}
public static void main(String[] args) {
int[] array=new int[]{34,23,49,3,45,100,324};
binInsertSort(array);
for(int item:array){
System.out.print(item+" ");
}
System.out.println();
binInsertSort2(array);
for(int item:array){
System.out.print(item+" ");
}
System.out.println();
shellSort(array);
for(int item:array){
System.out.print(item+" ");
}
}
}
希尔排序的时间复杂度、空间复杂度和稳定性分析
时间复杂度:最好:O(n) 最坏:O(n^2) 平均:O(n^1.2-1.5)(百科上写的是O(n^1.3-2)
空间复杂度:O(1)
稳定性:不稳定(因为不能保证相同数值的数被分到同一组中)