目录
直接插入排序
思想:将第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;
}