排序算法--插入篇(直接插入,二分插入,希尔)

一、插入类排序

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.此外还有其他多种取法,但没有最优性证明。

逆转数:为了分析希尔排序的优越性,这里引出逆转数的概念。对于待排序序列中的某个记录的关键字,它的逆转数是指在它之前比此关键字大的关键书的个数。

关键字4655134294170570
逆转数(Bi)00220461

在未经过一次希尔排序之前,它的逆转数之和为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.表名排序已完成。

 

 

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值