插入排序(直接插入,折半插入,希尔)

直接插入排序

 基本思想:假设有n个元素,当前考虑到第i个元素,前i-1个元素已经排好顺序,将第i个元素插入到前i-1个合适的位置。
 步骤:

1、设置一个“哨兵”把第i个元素放到"哨兵"上,比较的时候直接拿"哨兵"比较即可。
2、从i-1往前查找合适的位置,并把比当前大的元素往后挪。

void insert_sort(int n){
	int j;
	for(int i=2;i<=n;i++){
		if(arr[i]<arr[i-1]){//如果arr[i]>=arr[i-1]的话,那就没必要对元素i排序了。
			arr[0] = arr[i];//“哨兵”
			for(j = i-1;arr[0]<arr[j];j--){//从后往前查找
				arr[j+1] = arr[j];//第j个元素比arr[0]大,挪到第j+1位置上,继续往前查。
			}	
			//走到这一步说明arr[j]>=arr[0]了,所以arr[0]放到arr[j]的后边就可以了。
			arr[j+1] = arr[0];			
		}
	}
}

算法分析:
空间复杂度:O(1)
时间复杂度:O(n^2)
最好情况下:数组本身有序,不需要插入,比较n-1次就可以了。
最坏情况下:数组本身逆序,第i(i>=2)个元素需要 比较i次,挪动i+1次。
算法特点:稳定,可用于链式和顺序,当初始无序且n较大时,不适用。

折半插入排序

 基本思想:在原来直接插入排序的基础上,通过二分来确定第i个元素的合适的位置,这个位置就是从左到右首个严格大于arr[0]的元素(或者说是最后一个小于等于arr[i]的后边那个)然后这个位置就是元素arr[i]的合适位置(设为mid),然后将(mid–i-1)区间元素后移动一下。在把arr[i]放到arr[mid]上。

void insert_sort(int n){
	int l,r,high,low,mid;
	for(int i=2;i<=n;i++){
		if(arr[i]<arr[i-1]){
			arr[0] = arr[i];
			low = 1,high = i-1;
			while(low<=high){//二分找出第大于arr[0]的位置(或者是最后一个小于等于arr[0]的位置) 
				mid = (low+high)>>1;
				if(arr[mid]>arr[0]) {
//					ans = mid;//从左到右边第一个大于arr[0]的数的下标,那high一定是从左到
//								右最后一个小于等于arr[0]的数的下标。那么arr[0]应该放在high+1的处。 
					high = mid-1;
				}
				else low = mid+1;
			}
			for(int j = i-1;j>=high+1;j--){
				arr[j+1] = arr[j]; 
			}
			arr[high+1] = arr[0];
		}
	}
} 

算法分析:
时间复杂度:O(n^2)
空间复杂度:O(1)
插入第i个元素时,需要经过log(i)+1次比较。
算法特点:稳定排序,只能用与顺序表,适合初识记录无序,n较大的情况。

希尔排序

 基本思想:又叫做“缩小增量排序”,对数组按照一个逐级递减的步长分组,对每组进行直接插入排序,让数组大致有序,当步长减为1时,就是一个普通的直接插入排序。

void insert_sort(int n){
	int j;
	for(int dk = n/2;dk>=1;dk/=2){//步长 
		for(int i=dk+1;i<=n;i++){
//			先对第一组的元素排序,在对第二组,第三组....第一组,第二组.... 
			if(arr[i]<arr[i-dk]){//当前组元素arr[i]的前一个元素为arr[i-dk] 
				arr[0] = arr[i];
				for(j = i-dk;j>=0&&arr[j]>arr[0];j-=dk){
//加上j>=0是必要的,因为这j有可能取负值,当j取负值的时候说明arr[0]在当前组是最小的,只需要插入到当前组的第一个即可 
					arr[j+dk] = arr[j];
				}
				arr[j+dk] = arr[0];
			} 
		}
	}
}

算法分析:
空间复杂度:O(1)
时间复杂度:O(n^2)(有争议)
特点:不稳定,用于顺序不可用于链式,增量(步长)序列的取法无1之外的公因子,并且最后一个增量值为1。适合初始无序,n较大的情况。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值