外面下雨不方便出去,就趁着这个机会又把之前学过的、用过的排序算法给整理了一下。重新回过头来看看,倒是捡起了不少遗忘的知识,勤整理、勤汇总果然不会错。
#include<stdio.h>
#include<stdlib.h>
/*插入类排序*/
void insertSort(int a[],int n) //直接插入排序
{
int i,j;
int temp;
//第一个元素(i=0)默认有序,故从第二个元素(i=1)开始找位置插入
for(i=1; i<n; ++i)
{
//首先将待排记录暂存在temp中,
//避免后续的边比较边移位操作导致数据覆盖而丢失
temp=a[i];
//对有序序列的元素(a[i]之前的元素),temp从后往前与其比较,
j=i-1;
//j>=0防越界
//如果大于待排关键字,则后移一位
while(j>=0&&temp<a[j])
{
a[j+1]=a[j];
--j; //更新j继续比较,直至temp>=a[i]
}
//直到找到第一个大于temp的元素,插入记录到j指示位置的后面
a[j+1]=temp;
}
}
void insertSort2(int a[],int n)
{
int i,j;
//待排记录存放在1~n-1 ,从第2个元素开始比较
for(i=2; i<n; i++)
{
//a[0]功能:备份(类似于上面的temp)、防越界 (类似于上面的j>=0)
a[0]=a[i];
j=i-1;
while(a[0]<a[j])
{
a[j+1]=a[j];
j--;
}
a[j+1]=a[0];
}
}
void binSort(int a[],int n)//折半插入排序
{
int low,high,mid;
int i,j,x;
//待排记录存放在0~n-1,low、high分别指向前i-1个有序元素的首末元素,
//从第3个元素(i=2)开始与mid所指向的元素作比较
for(i=2; i<n; i++)
{
x=a[i];//待插记录
low=0;
high=i-1;
while(low<=high) //1.确定插入位置
{
mid=(low+high)/2;
//每次都只与mid的左半边或右半边作比较,减少了比较次数
if(x<a[mid])
high=mid-1;
else
low=mid+1;
}
for(j=i-1; j>=low; --j) //2.记录后移
a[j+1]=a[j];
a[low]=x; //3.插入记录
}
}
void shellInsert(int a[],int n,int delta)
{
int i,j;
//从第1个子序列的第2个元素开始,顺序扫描整个待排序列,当
//前元素属于哪个序列就对其在哪个序列上进行直接插入排序
//delta+1表示第1个子序列的第2个元素下标
for(i=delta+1; i<n; i++)
if(a[i]<a[i-delta])
{
a[0]=a[i];//只备份,不监视
//此处只能拿j>0做监视哨,因为j的变化以delta为增量,而不是减1,
//只有delta等于1时才可实现以a[0]作监视
//从后往前,与当前元素所在序列且位于当前元素前的元素比较,
//寻找最终该插入的位置(直接插入操作)
for(j=i-delta; j>0&&a[0]<a[j]; j-=delta)
a[j+delta]=a[j];
a[j+delta]=a[0];//插入记录
}
}
//希尔排序 :将待排序列分为若干个子序列,对子序列直接插入排序
void shellSort(int a[],int n,int delta[],int m)
{
int i;
for(i=0; i<m; i++) //依次以delta数组作为增量
shellInsert(a,n,delta[i]);
}
void shellSort2(int a[],int n)
{
int temp;
//gap表示希尔增量,初始为n/2,直至减小为1
for(int gap=n/2;gap>0;gap/=2)
{
for(int i=gap;i<n;++i)
{
temp=a[i];
int j;
for(j=i;j>=gap&&a[j-gap]>temp;j-=gap)
a[j]=a[j-gap];
a[j]=temp;
}
}
}
/*交换类排序*/
void bubbleSort(int a[],int n) //冒泡排序
{
int i,j;
int temp;
int flag;
for(i=n-1;i>=1;--i)
{
flag=0;
//从第二个元素开始,与其前一个元素比较,此时j可以一直移动到最后一个元素
for(j=1;j<=i;++j)
if(a[j-1]>a[j]) //比较相邻元素,逆序则交换
{
temp=a[j];
a[j]=a[j-1];
a[j-1]=temp;
flag=1;
}
/* for(i=1; i<n; i++) //n个元素最多比较n-1趟
{
flag=0;
//从第一个元素开始,与其后一个元素比较 ,此时j只能移到倒数第二个元素
for(j=0; j<n-i; j++)
{
//比较相邻元素,逆序则交换
flag=1;
}
*/
//若某一趟比较中没有发现一个逆序,则直接结束整个排序过程
if(flag==0)
return;
}
}
void quickSort(int a[],int low,int high) //快速排序 :一次消除多个逆序
{
int temp;
int i=low,j=high;//low high分别指向数组首末
if(low<high)
{
temp=a[low];//假设首元素为枢轴 。此时a[low]看做空单元
while(i<j)
{
//首先,j从后往前直到找出比枢轴小的元素的位置
while(i<j&&a[j]>=temp)
--j;
//移入空单元a[low]中 ,此时a[high]为空单元
if(i<j)
{
a[low]=a[j];
++i;
}
while(i<j&&a[i]<temp)//i从前往后直到找到比枢轴大的元素,然后交换
++i;
//移入空单元a[high]中 ,此时a[low]为空单元
if(i<j)
{
a[high]=a[i];
--j;
}
}
a[i]=temp;//将枢轴放到最终确定的位置,即i=j的位置
//一次划分后,将待排序列分为两个子序列
quickSort(a,low,i-1);//对temp左边的子序列排序
quickSort(a,i+1,high);//对temp右边的子序列排序
}
}
/*选择类排序*/
//简单选择排序:第i趟选择排序,从第i个元素开始,比较n-i次,
//从n-i+1个元素中找到关键字最小(大)的并和第i个元素交换
void selectSort(int a[],int n)
{
int i,j,k;
int temp;
for(i=0; i<n; i++) //共n趟
{
k=i;//初始时总是假设当前无序序列的第一个元素为最小值
for(j=i+1; j<n; ++j) //找出值最小的元素的位置
if(a[k]>a[j])
k=j;
//最小关键字与当前无序序列的第一个关键字交换
temp=a[i];
a[i]=a[k];
a[k]=temp;
}
}
void shift(int a[],int i,int n)//筛选法调整大根堆
{
int temp=a[i];
//从i节点的左孩子开始,即从2i+1开始
for(int j=2*i+1; j<n; j=2*j+1)
{
//如果存在右孩子 ,且左孩子小于右孩子,j指向右孩子
if(j+1<n&&a[j]<a[j+1])
j++;
//如果孩子节点大于双亲节点,直接将子节点值赋给双亲节点
if(a[j]>temp)
{
a[i]=a[j];
i=j;
}
else
break;
}
a[i]=temp;//将temp放到最终的位置
}
void shift1(int a[],int low,int high)
{
int i=low,j=2*i+1;
int temp=a[i];
while(j<=high)
{
if(j<high&&a[j]<a[j+1])
++j;
if(temp<a[j])
{
a[i]=a[j];
i=j;
j=2*i+1;
}
else
break;
}
a[i]=temp;
}
//堆排序 。任意元素序列看做完全二叉树,升序则建立大根堆,降序则建立小根堆
void heapSort(int a[],int n)
{
int temp;
//建立初始堆 :筛选法。
//i从0开始。故从第n/2-1个元素(最后一个非叶子元素)开始逐层向上倒退,
//直到根节点
for(int i=n/2-1; i>=0; i--)
//shift(a,i,n);
shift1(a,i,n-1);
//交换堆顶元素与末尾元素,继续调整堆结构
for(int j=n-1; j>0; j--)
{
temp=a[0];
a[0]=a[j];
a[j]=temp;
//shift(a,0,j);
shift1(a,0,j-1);
}
}
void Sift(int a[],int low,int high)//low,high表示调整范围
{
int i=low,j=2*i;//i指向双亲,j为其左孩子
int temp=a[i];//对位置i上的节点进行调整
while(j<=high)//至少得有左孩子才进入循环
{
//如果也有右孩子,且左孩子小于右孩子,则j指向右孩子
if(j<high&&a[j]<a[j+1])
++j;
//如果孩子节点中的较大者大于其双亲,则该孩子被交换到双亲位置上
if(temp<a[j])
{
a[i]=a[j];
i=j; //i,j向下平移一个位置
j=2*i;
}
else
break;
}
a[i]=temp;//被调整节点放到最终位置
}
void heapSort2(int a[],int n)
{
int i;
int temp;
for(i=n/2;i>=1;--i)//从最后一个非叶子节点开始调整 ,建立初始堆
Sift(a,i,n);
for(i=n;i>=2;--i)//进行n-1次循环,完成堆排序
{
temp=a[1];//换出根节点的关键字,放入最终位置
a[1]=a[i];
a[i]=temp;
Sift(a,1,i-1);
}
}
void merge(int array[], int p, int q, int r) //动态申请数组空间
{
int n1 = q - p + 1;//子序列1的长度
int n2 = r - q;//子序列2的长度
int *L;
L = (int*)malloc(sizeof(int)*n1);
int *R;
R = (int*)malloc(sizeof(int)*n2);
int i = 0; //i,j分别指向两个子序列
int j = 0;
for(;i < n1; i++)//L,R暂存两子序列的元素
L[i] = array[i + p];
for(;j < n2; j++)
R[j] = array[j + q +1];
i = j = 0;
int k = p;
while(i!=n1 && j!= n2) //比较两子序列的元素,较小者保存到数组中
{
if(L[i] <= R[j])
array[k++] = L[i++];
else
array[k++] = R[j++];
}
while(i < n1)//序列1还有元素
array[k++] = L[i++];
while(j < n2)//序列2还有元素
array[k++] = R[j++];
free(L);
free(R);
}
void merge2(int a[],int low,int mid,int high)
{
int i,j,k;
int n1=mid-low+1;
int n2=high-mid;
int L[n1],R[n2];
for(i=0;i<n1;i++)
L[i]=a[low+1];
for(j=0;j<n2;j++)
R[j]=a[mid+i+j];
i=0;
j=0;
k=low;
while(i<n1&&j<n2)
{
if(L[i]<R[j])
a[k]=L[i++];
else
a[k]=R[j++];
k++;
}
while(i<n1)
a[k++]=L[i++];
while(j<n2)
a[k++]=R[j++];
}
//2-路归并排序 :先不断2分(未必完全平均)直至每个小序列只有一个元素为止,然后两两合并
void mergeSort(int a[],int low,int high)
{
if(low<high) {
//分解过程
int mid=(low+high)/2;
mergeSort(a,low,mid);
mergeSort(a,mid+1,high);
//合并过程
merge2(a,low,mid,high);
}
}
//传入暂存数组 ,这个更好理解
void Merge2(int sourceArr[],int tempArr[], int startIndex, int midIndex, int endIndex)
{
int i = startIndex, j=midIndex+1, k = startIndex;
while(i!=midIndex+1 && j!=endIndex+1)
{
if(sourceArr[i] > sourceArr[j])
tempArr[k++] = sourceArr[j++];
else
tempArr[k++] = sourceArr[i++];
}
while(i != midIndex+1)
tempArr[k++] = sourceArr[i++];
while(j != endIndex+1)
tempArr[k++] = sourceArr[j++];
//最终排序好的元素还是保存回原数组,tempArr只用于暂时保存用
for(i=startIndex; i<=endIndex; i++)
sourceArr[i] = tempArr[i];
}
void MergeSort2(int sourceArr[], int tempArr[], int startIndex, int endIndex)
{
int midIndex;
if(startIndex < endIndex)
{
midIndex = startIndex + (endIndex-startIndex) / 2;//避免溢出int
MergeSort2(sourceArr, tempArr, startIndex, midIndex);
MergeSort2(sourceArr, tempArr, midIndex+1, endIndex);
Merge2(sourceArr, tempArr, startIndex, midIndex, endIndex);
}
}
void printArray(int a[],int n) {
int i;
for(i=0; i<n; i++)
printf("%d\t",a[i]);
printf("\n");
}
int main() {
int a[5]= {2,1,4,3,6};
int b[8]= {0,1,4,3,6,7,2,9};
int c[8]= {8,62,35,77,55,14,36,98};
printf("直接插入排序1:\n");
insertSort(a,5);
printArray(a,5);
printf("\n直接插入排序2:\n");
insertSort2(b,8);
printArray(b,8);
printf("\n折半插入排序:\n");
binSort(a,5);
printArray(a,5);
printf("\n希尔排序:\n");
//int delta[3]= {4,2,1}; //n/2
//shellSort(b,8,delta,3);
shellSort2(b,8);
printArray(b,8);
printf("\n冒泡排序:\n");
bubbleSort(a,5);
printArray(a,5);
printf("\n快速排序:\n");
quickSort(a,0,4);
printArray(a,5);
printf("\n简单选择排序:\n");
selectSort(a,5);
printArray(a,5);
printf("\n堆排序:\n");
heapSort(c,7);
printArray(c,8);
printf("\n2-路归并排序:\n");
int d[8];
MergeSort2(c,d,0,7);
//mergeSort(c,0,7);
printArray(c,8);
return 0;
}