【C语言】插入类排序之直接插入排序、折半插入排序、希尔排序分析及输出每趟排序的结果

目录

直接插入排序

折半插入排序

希尔排序

完整代码


直接插入排序

        思想:将第i个记录插入到前i-1个已经排好的有序序列中,记录数组中arr[0]作为哨兵,存放当前待插入记录,防止越界以及被其他记录覆盖,首个记录arr[1]默认有序,将待插记录依次和前方记录对比,如果待插记录小于当前比较记录时,当前记录后移,直至大于等于当前比较记录时,插入其后即可,保持排序的稳定性。

void InsertSort(int arr[],int length){//对待排序列arr进行直接插入排序
	for(int i=2;i<=length;i++){//将第i个记录插入前i-1个有序的序列中,arr[0]作哨兵,首个默认有序
		arr[0]=arr[i];//待插记录存入哨兵,防止越界以及被覆盖
		int j=i-1;
		while(arr[j]>arr[0]){//待插记录依次和前方记录比较调整,等于时不做调整保证稳点性
			arr[j+1]=arr[j];
			j--;
		}
		arr[j+1]=arr[0];
		printf("第%d次排序的结果为:\n",i-1);
		PrintArr(arr,length);
	}
}

         时间复杂度:当待排记录已经从小到大顺序排列时为最好情况,此时每一趟仅需要比较1次,总共比较n-1次即可,不使用哨兵则不要移动,使用则每趟移动2次(arr[0]=arr[i]、arr[j+1]=arr[0])共移动2(n-1)次。最坏情况即待排记录从大到小逆序有序排列,第1躺比较2次,移动3次,第i躺比较i+1次,移动i+2次,总需要比较2+3+....+n=(n+2)(n-1)/2次,需要移动3+4+....+n+1=(n+4)(n-1)/2次,平均时间复杂度为T(n)=O(n^2)

折半插入排序

        思想:折半插入排序同直接插入排序一样,仅仅只是使用折半查找来确定待排序记录的插入位置,减少了关键字的比较次数,但是在找到后需要依次移动后续元素,未改善元素移动的次数。

void BinSort(int arr[],int length){//折半插入排序
	for(int i=2;i<=length;i++){//折半插入排序即利用折半查找确定待插记录的位置在进行插入,优化了直接插入减少对比次数
		arr[0]=arr[i];//待插记录存入哨兵,防止越界以及被覆盖
		int low=1;
		int high=i-1;
		while(low<=high){//折半查找确定插入位置
			int mid=(low+high)/2;
			if(arr[0]>=arr[mid]){//等于时向右保证稳点性
				low=mid+1;
			}
			else{
				high=mid-1;
			}
		}
		for(int j=i-1;j>=low;j--){//当low>high时找到插入位置,元素依次后移
			arr[j+1]=arr[j];
		}
		arr[low]=arr[0];//插入待插记录
		printf("第%d次排序的结果为:\n",i-1);
		PrintArr(arr,length);
	}
}

        时间复杂度:采用折半插入排序仅仅减少了关键字的比较次数,比较次数最大为折半判定树的深度即log2n+1,插入n个元素的平均比较次数为nlog2n,但并未改变移动次数,平均时间复杂度依然为T(n)=O(n^2)

希尔排序

        思想:根据规定的记录间隔d,将待排序列分隔为若干个子序列,对每个子序列都进行一次直接插入排序,排序后更新d的值为之前的一半直至d=1,再对最后基本有序的序列进行一次直接插入排序即可,一般设置d初始为记录数目的一半,直接插入排序可视为d=1的希尔排序。希尔排序不能保证稳定性例如:2,4,1,2

void ShellSort(int arr[],int length){//希尔排序
	for(int d=length/2;d>=1;d=d/2){//根据设置的记录间距d分为若干子序列进行插入排序
		for(int i=d+1;i<=length;i++){//从当前子序列的第二个位置开始直接插入,并更换不同的子序列
			arr[0]=arr[i];//待插记录存入哨兵,防止越被覆盖
			int j=i-d;//与当前子序列的值比较
			while(arr[j]>arr[0]){
				arr[j+d]=arr[j];
				j-=d;//更新j为下一个子序列的值
				if(j<0){//越界判断
					break;
				}
			}
			arr[j+d]=arr[0];//插入待排记录
		}
		static int nums=1;
		printf("第%d次排序的结果为:\n",nums);
		PrintArr(arr,length);
		nums++;
	}
}

         时间复杂度:尽管d=1时等同于直接插入排序但是此时待排序列已经基本有序,效率对于直接插入排序也有提升,一般认为时间复杂度为T(n)=O(n^1.5)。

完整代码

#include<stdio.h>
void InsertSort(int*,int);//直接插入排序
void BinSort(int*,int);//折半插入排序
void ShellSort(int*,int);//希尔排序
void PrintArr(int*,int);//输出记录
void InsertSort(int arr[],int length){//对待排序列arr进行直接插入排序
	for(int i=2;i<=length;i++){//将第i个记录插入前i-1个有序的序列中,arr[0]作哨兵,首个默认有序
		arr[0]=arr[i];//待插记录存入哨兵,防止越界以及被覆盖
		int j=i-1;
		while(arr[j]>arr[0]){//待插记录依次和前方记录比较调整,等于时不做调整保证稳点性
			arr[j+1]=arr[j];
			j--;
		}
		arr[j+1]=arr[0];
		printf("第%d次插入排序的结果为:\n",i-1);
		PrintArr(arr,length);
	}
}
void BinSort(int arr[],int length){//折半插入排序
	for(int i=2;i<=length;i++){//折半插入排序即利用折半查找确定待插记录的位置在进行插入,优化了直接插入减少对比次数
		arr[0]=arr[i];//待插记录存入哨兵,防止越界以及被覆盖
		int low=1;
		int high=i-1;
		while(low<=high){//折半查找确定插入位置
			int mid=(low+high)/2;
			if(arr[0]>=arr[mid]){//等于时向右保证稳点性
				low=mid+1;
			}
			else{
				high=mid-1;
			}
		}
		for(int j=i-1;j>=low;j--){//当low>high时找到插入位置,元素依次后移
			arr[j+1]=arr[j];
		}
		arr[low]=arr[0];//插入待插记录
		printf("第%d次折半排序的结果为:\n",i-1);
		PrintArr(arr,length);
	}
}
void ShellSort(int arr[],int length){//希尔排序
	for(int d=length/2;d>=1;d=d/2){//根据设置的记录间距d分为若干子序列进行插入排序
		for(int i=d+1;i<=length;i++){//从当前子序列的第二个位置开始直接插入,并更换不同的子序列
			arr[0]=arr[i];//待插记录存入哨兵,防止越被覆盖
			int j=i-d;//与当前子序列的值比较
			while(arr[j]>arr[0]){
				arr[j+d]=arr[j];
				j-=d;//更新j为下一个子序列的值
				if(j<0){//越界判断
					break;
				}
			}
			arr[j+d]=arr[0];//插入待排记录
		}
		static int nums=1;
		printf("第%d次希尔排序的结果为:\n",nums);
		PrintArr(arr,length);
		nums++;
	}
}
void PrintArr(int arr[],int length){//输出记录序列
	for(int i=1;i<=length;i++){
			printf("%d ",arr[i]);
		}
	printf("\n");
}
int main(){
	int arr[]={00,55,88,66,33,99,44,77,11,22};
	int length=sizeof(arr)/sizeof(arr[0])-1;
	printf("排序前为:\n");
	PrintArr(arr,length);
//	InsertSort(arr,length);//直接插入排序
//	BinSort(arr,length);//折半插入排序
	ShellSort(arr,length);//希尔排序
	printf("排序后为:\n");
	PrintArr(arr,length);
	return 0;
}

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值