一、插入类排序
1.直接插入排序
算法思想:基本操作是将第i个记录插入到前面第i-1个已排序好的记录中。具体过程:把第i个记录的关键字Ki,依次与前面Ki-1,Ki-1,...,K1比较,将所有关键字大于Ki的记录依次先后移动一个位置,直到遇到一个关键字Kj小于或等于关键字Ki,此时Kj后面必为空把第i个元素插入进去即可。
private static void InsSort(int[] a) {
int key=0,j=0;
for (int i = 1; i < a.length; i++) {
key=a[i];//将待插入记录存放到监视哨key中
j=i-1;
while(j>=0&&key<a[j]){//寻找插入位置
a[j+1]=a[j];
j=j-1;
}
a[j+1]=key;
}
}
算法分析:从空间角度来看,只需要一个辅助空间key,空间复杂度S(n)=O(1);
从时间角度来看,最好情况while循环只进行一次,总比较次数为n-1次,最坏是比较(n+2)(n-1)/2,时间复杂度为T(n)=O(n^2)
稳定性:稳定
改进:待排序记录数目较大时,可以在此基础上,从“减少关键字”和“移动记录”两种操作的次数进一步改进
2.折半插入排序
算法思想:在有序记录a[1,..i-1]中,采用折半查找确定插入的位置
private static void BinSort(int[] a) {
int k,low,high,mid;
for (int i = 1; i < a.length; i++) {
k= a[i];
low=0;
high=i-1;
while(low<=high){//确定插入位置
mid=(low+high)/2;
if(k<a[mid]) high=mid-1;
else low=mid+1;
}
for(int j=i-1;j>=low;--j) a[j+1]=a[j];//记录依次向后移动
a[low]=k;//插入记录
}
}
算法分析:采用折半插入排序法,可减少关键字的比较次数。每插入一个元素,需要比较的次数最大为折半判定树的深度,如插入第i个元素时,设i=2^j,则需进行log2i次比较,因此插入n-1个元素的平均关键字的比较次数为nlog2n。
空间复杂度:O(1) 时间复杂度O(n^2) 稳定
3.希尔排序
算法思想:对直接插入排序的改进,又称缩小增量排序法。先将待排序记录序列分割成若干个“较稀疏的”子序列,分别进行直接插入排序。最后再对全部记录进行一次直接插入排序。
具体流程:
1.将包含n个元素的数组,分成n/2个数组序列,第一个数据和第n/2+1个数据为一对
2.对每对数据进行比较和交换,排好顺序
3.然后分成n/4个数组序列,再次排序
4.不断重复以上过程,随着序列减少并直至为1,排序完成。
假设初始数据为:【25,11,45,13,66,9】,
1.第一轮排序,将该数组分为6/2=3个数组序列,第1个数据和第4个数据为一对,第2个数据和第5个数据为一对,第3个和第6为一对,排序后[13,11,9,25,66,45]
2.第二轮排序,将上轮排序分为6/4=1个数组序列,[9,11,13,25,45,66]
private static void ShellSort(int[] a) {
if(a == null || a.length <= 1){
return;
}
//增量
int increNum = a.length/2;
while(increNum >=1){
for(int i=0;i<a.length;i++){
//进行插入排序
for(int j=i;j<a.length-increNum;j=j+increNum){
if(a[j]>a[j+increNum]){
int temple = a[j];
a[j] = a[j+increNum];
a[j+increNum] = temple;
}
}
}
//设置新的增量
increNum = increNum/2;
}
}
算法分析:当增量为1时,与直接插入排序过程相同。在希尔排序中,各子序列的排序过程相对独立,但具体实现时,并不是先对一个子序列进行完全排序,再对另一个子序列进行排序。在顺序扫描整个待排序记录序列时,各子序列的元素将会反复轮流出现。根据这个特点,希尔排序从第一个子序列的第二个元素开始,顺序扫描待排序记录序列,对首先出现的各子序列的第二个元素,分别在各子序列中进行插入处理;然后对随后出现的各子序列的第3个元素,分别在各子序列中进行插入处理,知道处理完各子序列的最后一个元素。
时间复杂度O(n^1.25) 空间复杂度O(1) 不稳定
扩:
增量的取法:关于增量d的取法,最初希尔(Shell)提出取d=[n/2],再取d=[d/2],知道d=1为止。该思路的缺点是,奇数位置的元素在最后一步才会与偶数位置的元素进行比较,使得希尔排序效率降低。因此后来Knuth提出d=[d/3]+1.此外还有其他多种取法,但没有最优性证明。
逆转数:为了分析希尔排序的优越性,这里引出逆转数的概念。对于待排序序列中的某个记录的关键字,它的逆转数是指在它之前比此关键字大的关键书的个数。
关键字 | 46 | 55 | 13 | 42 | 94 | 17 | 05 | 70 |
---|---|---|---|---|---|---|---|---|
逆转数(Bi) | 0 | 0 | 2 | 2 | 0 | 4 | 6 | 1 |
在未经过一次希尔排序之前,它的逆转数之和为0+0+2+2+0+4+6+1=15,之后经过一次希尔排序后得到[46,17,05,42,94,55,13,70],其中17和55位置发生了变化,对17之前和55之后的关键字的逆转数无影响,而两个关键字本身以及介于这两个关键字之间的逆转数都会减少。对于17本身而言,其逆转数必减少l,而关键字17和55之间的关键字,若其值大小界于17和55之间,则这些关键字的逆转数一定会减少。即假设被调换位置的两个关键字之间有l个界于两个关键字之间的数,则逆转数之和一定会减小2l+1.
以上面序列为例,初始逆转和为15,经d1=4后,逆转数1+2+1+0+1+5+1=11;经d2=2后,逆转数0+1+0+0+0+1=2;经d3=1后,逆转数为0.表名排序已完成。