二分插入(排序)

在PTA排序题中,由于冒泡法超时,采用了二分插入法以降低时间复杂度到O(nlogn)。该算法通过二分查找确定新数据在递增数组中的位置并插入。具体步骤包括初始化lower和upper,根据新数据与数组中值的比较调整边界,最终确定插入位置。当upper和lower差值为1时,新数据插入upper位置。代码实现验证了这一过程。
摘要由CSDN通过智能技术生成

最近在PTA上遇到排序题,题目为:

 刚开始想用冒泡法进行排序,发现冒泡法排序测试时间为10s,超出题目所限时长,冒泡法的时间复杂度为O(n^2),为了进一步降低题目的时间复杂度,突发奇想,借鉴二分查找的思想,建立了二分插入法,其时间复杂度为O(nlogn)。

其基本思路为:首先建立一个题目所要求大小的数组,每当输入一个,新的数据,采用二分法的思路查找新数据在递增数组中的位置,然后插入数组。实现的具体步骤为:

对于第i个数据number(对应数组大小为i+1),应插入序号为p

  1. lower=0;upper=max(i-1,0);  

  2. 若number<=a[0];p=0;//新数据在0位

  3. 若number>=a[upper],p=i;//新数据在第i位

  4. 若upper-lower=1,结束循环,p=upper;//新数据在lower和upper之间

  5. 若numer=a[(upper+lower)/2),p=(upper+lower),跳出循环; //新数据在(upper+lower)/2

  6. 若number>a[(upper+lower)/2],lower=(upper+lower)/2;

  7. 若number<a[(upper+lower)/2],upper=(upper+lower)/2;

  8. 返回第4步;

二分插入与二分查找最大的区别在于新数据可能在原数组中没有重复的数据,新数据位于两个数据元素之间,首先判断新数据是否大于或等于原数组的最大值或者最小值,排除了lower和upper在数组两端的可能性,之后upper和lower都由(upper+lower)/2赋值过来,因此不可能等于upper和lower对应的数组元素,当upper和lower差值位1时,新数组必然在a[lower]和a[upper]之间,应插入位置为p=upper。

代码如下:

#include <stdio.h>
#include <stdlib.h>
int main(){
	int n=6;
	//int b[]={5,4,3,2,1,3};
    scanf("%d",&n);
	int* a=(int*)malloc(n*sizeof(int));
	int i=0;
	int number;
	a[0]=0;
	int upper=0;
	int lower=0;
	int p=0;
	for(i=0;i<n;i++){
		scanf("%d",&number);
	//	number=b[i];
	//	number=rand();
		lower=0;
		upper=((i-1)<0)?0:(i-1);
		int isFound=0;
		if(number<=a[0]) {
		p=0;
		isFound=1;
		}
		else if(number>=a[((i-1)<0)?0:(i-1)]){
		p=i;
		isFound=1;
		}
		else{
		while(upper>lower+1){
		 if(number==a[(upper+lower)/2]){
		 	p=(upper+lower)/2;
		    isFound=1;
			break;	
		 }
		else if(number>a[(upper+lower)/2]){
			lower=(upper+lower)/2;
		}
		else {
			upper=(upper+lower)/2;
		}	
	  }
	}
	     if(isFound==0) p=upper;
	     int j=0;
	     for(j=i;j>p;j--){
	     	a[j]=a[j-1];
	     	
		 }
		a[p]=number;
		/*if(a[p]<a[p-1<0?0:p-1]){
		   printf("在a[%d]=%d和a[%d]=%d中插入%d,此时i=%d,upper=%d,lower=%d,isFound=%d\n,",p-1<0?0:p-1,a[p-1<0?0:p-1],p+1>i?i:p+1,a[p+1>i?i:p+1],a[p],i,upper,lower,isFound);
		   int j=0;
		   for(j=lower;j<=upper+1;j++)  printf("a[%d]=%d  ",j,a[j]);
		   printf("\n\n");
	}*/
	}
//nt error=0;
for(i=0;i<n-1;i++){
//	if(a[i]>a[i+1]) {
//	error=1;
//	printf("%d > %d\t",a[i],a[i+1]);}
	printf("%d ",a[i]);
}
printf("%d",a[i]);
free(a);
	
return 0;	
} 

最后总结一下二分缩近的规律:

  1. 2->{1,3}------->{1,2,3},p=upper;
  2. 2->{1,3,4}------>2->{1,3}(第一步)
  3. 2->{0,1,3,4}------->2->{1,3,4}(第二步)

因此当新数据在原数组中没有相等的数据是,upper和lower最终会等于1,如果有相等数据,则会率先满足number=a[(upper+lower)/2],跳出循环。

评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值