内部排序之插入排序

	内存有限   数据量比较大的时候   
	如果是对内存中的数据排序:内部排序
	如果是对外存中的数据进行排序:外部排序


内部排序
	4G的虚拟内存

直接插入:

	从第一个元素开始,到最后一个元素,依次把数组中的元素插入到前面,使之保持有序
	例:1024 999 1212 8888  521 1314 9527
	一开始1024前面没有元素
	1024     第一次              
	999 1024…  第二次
	下标为i的元素要插入到[0,i-1]区间,使插入arr[i]元素局部[0,i]区间保持有序
	i+1 插入到[0,i]   是[0,i+1]有序
	把前面的数组作为有序序列,逐次插入一个元素之后,使之保持有序
//时间复杂度O(n^2)  空间复杂度O(1)
void insertSort(int arr[],int n){
	int i,j;
	//i=0的时候只有一个元素,本身就有序,所以不需要插入,
	//所以从i=1开始,表示从第二个元素开始,逐一往前插入,插入后使之保持有序

	for(i=1;i<n;i++){//i=1  1+2+3+4+...+n-1    n(n-1)/2
		int key = arr[i];//提前保存要插入的元素 因为前面的元素往后移,会覆盖
		//[0,i-1]有序  插入key之后使   [0,i]区间有序
		for(j=i-1;j>=0&&arr[j]>key;--j){
			arr[j+1] = arr[j];
		} 
		//if(i!=j+1){//判断其实不需要,因为本身j就是按规则在变
		arr[j+1] = key;   //arr[i] = key; 
		//}
	} 
}

如何分析排序(从3个方面):

1.时间复杂度
2.空间复杂度
3.稳定性:原始数量列中有Ri和Rj两个元素,数据元素相等,Ri==Rj,
且i<j ,i和j表示数据元素Ri,Rj的位置  Ri元素在Rj元素前面,
如果经过某种排序后,得到Ri的元素的位置为i’,Rj元素的位置为j’。
如果i’<j’,排序之后,Ri元素依然在Rj前面,称这种排序为稳定排序  否则i’和j’为不稳定排序。
	
稳定性无关一个排序的好坏,只是考虑特定的场合
稳定的排序一定是好的排序吗?不一定
不稳定的一定是不好的吗?不一定

折半插入

//时间复杂度O(n^2)  空间复杂度是O(1) 
void binInsertSort(int arr[],int n){
	int left,right,mid,i,j,key;
	for(i=1;i<n;i++){//arr[1]----arr[n-1]所有的元素依次往前插入 
		key = arr[i];//保存要插入的元素 arr[i]   arr[0]--arr[i-1] 
		for(left=0,right=i-1;left<=right;){//找到arr[i]插入的位置  折半查找 
			mid = (left+right)/2;
			if(key < arr[mid]){
				right = mid-1;
			}else{
				left = mid+1;
			}
		}
		//right+1 位置要存储arr[i] key 
		for(j=i-1;j>right;--j){
			arr[j+1] = arr[j];
		}
		arr[right+1] = key;
	}
}
(这是效率最低的情况)
原始数列 10 9 8 7 6 要求升序
原始序列和最终的序列正好相反的情况,每次插入一个元素,都需要把该元素插入到最前面,
需要把该元素之前所有的元素都往后移动
10 9 
9 10 8
8 9 10 7 
7 8 9 10

2-路插入排序

1212  618  8888  999  9527  1111  1314  521 555  1024
brr数组的左边和右边
max                                 min
1024, 1212,1314,8888, 9527         521, 555,618,999,1111,
申请和原始数列同样大的内存空间,最开始往左插入,遇到比brr[0]更小的值,插入到右边
第二个小的元素放到第二个数组的最右边right[n-1]
这个方法就不用每次都全部移动,比上面的那个少一点
//时间复杂度O(n^2)  空间复杂度O(n) 
void twoRoadInsert(int arr[],int n){
	int *brr = (int *)malloc(sizeof(arr[0])*n);//brr
	int left = -1,right = n;
	int i,j;
	for(i=0;i<n;i++){
		if(left==-1||arr[i]>brr[0]){//大于最左端的元素 
			for(j=left;j>=0&&arr[i]<brr[j];--j){
				brr[j+1] = brr[j];
			}
			brr[j+1] = arr[i];
			++left;
		}else{//小于brr[0] 如果要插入左边 左边所有的元素都要移动 
			for(j=right;j<n&&arr[i]>brr[j];++j){
				brr[j-1] = brr[j];
			}
			brr[j-1] = arr[i];
			--right;
		} 
	}
	for(i=right;i<n;i++){
		arr[i-right] = brr[i];
	}
	for(i=0;i<=left;i++){
		arr[n-right+i] = brr[i];
	}
	free(brr);
}

希尔排序

9527 1314 8888 1212 521 555 618 999  1024 1111

1212 1314 8888 9527
每次插入 9527离最终的位置近了一步
10 元素  分组10/2  5分组
9527 555    1314 618    8888 999      1212 1024     521 1111
9527 1314 8888 1212 521 555 618 999  1024 1111
555                    9527
555   618              9527  1314
555   618 999 1024  521    9527  1314   8888  1212 1111
		
		再2组  5/2
		555 999   521  1314  1212
		618 1024  9527  8888  1111
		组内排序按顺序插回原来的队列中

521   618    555   1024     999    1111    1212    8888     1314    9527
		
		
		再1组2/2
		组内排序
//时间复杂度是O(nlogn)    希尔排序  O(n^1.3)  空间复杂度O(1)  不稳定 
void shellSort(int arr[],int n){
	//分组  步长    分为step小组   组内元素步长step 
	int step,i,j;
	for(step = n/2;step>0;step=step/2){
		//组内排序  [0,step) 组内的第一个元素 
		for(i=step;i<n;i++){//除了组内第一个元素 其它元素都需要进行组内插入 
			int key = arr[i];
			for(j=i-step;j>=0&&key<arr[j];j=j-step){
				arr[j+step] = arr[j];//组内的元素往移 
			} 
			arr[j+step] = key; 
		} 
	} 	
}

main.c

void showArr(int arr[],int len){
	int i;
	for(i=0;i<len;i++){
		printf("%d ",arr[i]);
	}
	printf("\n");
}
int main(int argc, char *argv[]) {
	//数组为10,元素只有9个 
//	int arr[10] = {521,618,999,1024,1111,1212,1314,8888,9527};//555 
//	int key = 0;
//	scanf("%d",&key);
//	insertNum(arr,10,key);
	int arr[10] = {1212,618,8888,999,9527,1111,1314,521,555,1024};
	//insertSort(arr,10);
	//binInsertSort(arr,10);
	//twoRoadInsert(arr,10);
	shellSort(arr,10);
	showArr(arr,10);
	return 0;
}
有一组升序的数据,插入一个数据元素,使之插入该序列后保持升序
从最后一个元素开始比较,如果数组中的元素比要插入的元素key大,
则把数组中该元素往后移动一个位置,知道数组中所有元素比较完  或者 遇到比key小的则循环退出
key插入的位置为最后一次比较元素(位置i)的后一个位置(i+1)
void insertNum(int arr[],int len,int key);
//arr数组宏原来有Len-1个元素,[0,len-2] 升序
	插入一个元素Key,使插入之后依然有序

普通插入保持有序

//arr是升序  插入key之后 依然要保持升序 
void insertNum(int arr[],int len,int key){
	if(len==1)
		return;
	int i;
	for(i=len-2;i>=0&&arr[i]>key;--i){//arr[i]比key要大,说明key在arr[i]前面 
		arr[i+1] = arr[i];//arr[i]要往后移一个位置 
	}
	//i==-1  key比数组中所有元素都要小  arr[0]
	//i>=0  arr[i]<=key  key要放在arr[i]后面 arr[i+1] 
	arr[i+1] = key;
} 

折半插入保持有序

void insertNum(int arr[],int len,int key){
	int left,mid,right;
	for(left=0,right=len-2;left<=right;){//left>right
		mid = (left+right)/2;
		if(key<arr[mid]){
			right = mid-1;
		}else{
			left = mid+1;
		}
	} 
	int i;
	for(i=len-2;i>right;--i){
		arr[i+1] = arr[i];
	}
	arr[right+1] = key;	
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值