简单排序
#include <stdio.h>
#include <stdlib.h>
#define MAXN 10
void swap(int a[],int i,int j)
{
int t = a[i];
a[i] = a[j];
a[j] = t;
}
int main()
{
int num[MAXN] = {9,1,5,8,3,7,4,6,2,10};
for(int i = 0; i < MAXN; ++i)
{
for(int j = MAXN-1; j > i; --j)
if(num[j] < num[i])
swap(num,i,j);
}
for(int i = 0; i < MAXN; ++i)
printf("%d ",num[i]);
return 0;
}
冒泡排序
普通冒泡排序
能够有效的减少swap函数的调用次数,提高速度。
#include <stdio.h>
#include <stdlib.h>
#define MAXN 10
void swap(int a[],int i,int j)
{
int t = a[i];
a[i] = a[j];
a[j] = t;
}
int main()
{
int num[MAXN] = {9,1,5,8,3,7,4,6,2,10};
for(int i = 0; i < MAXN-1; ++i) //前面排序好了最后一个位置不用排序
{
for(int j = MAXN - 2; j >= i; --j) //从尾向头开始才能保证位置i为最小的数
if(num[j] > num[j+1])
swap(num,j+1,j);
}
for(int i = 0; i < MAXN; ++i)
printf("%d ",num[i]);
return 0;
}
优化冒泡排序
当没有发生位置互换时其实已经代表后面的数也是排序好的了,所以不用再循坏了
#include <stdio.h>
#include <stdlib.h>
#define MAXN 10
void swap(int a[],int i,int j)
{
int t = a[i];
a[i] = a[j];
a[j] = t;
}
int main()
{
int num[MAXN] = {9,1,5,8,3,7,4,6,2,10};
int flag = 1;
for(int i = 0; i < MAXN-1 && flag; ++i)
{
flag = 0;
for(int j = MAXN-2; j >= i ; --j)
{
if(num[j] > num[j+1])
{
swap(num,j,j+1);
flag = 1;
}
}
}
for(int i = 0; i < MAXN; ++i)
printf("%d ",num[i]);
return 0;
}
时间复杂度
最优:第一次i循坏就排序完成——O(n)
最差:需要比较 i-1 (i:2~10)的总和,为(n-1)*(n-1)/2——O(n^2)
选择排序
#include <stdio.h>
#include <stdlib.h>
#define M 10
void swap(int a[],int i,int j)
{
int t = a[i];
a[i] = a[j];
a[j] = t;
}
int main()
{
int num[M] = {9,1,5,8,3,7,4,6,2,10};
int min;
for(int i = 0; i < M-1; ++i)
{
min = i;
for(int j = i+1; j < M; ++j)
if(num[j] < num[min])
min = j;
if(min != i)
swap(num,i,min);
}
for(int i = 0; i < M; ++i)
printf("%d ",num[i]);
return 0;
}
时间复杂度
需要比较 i-1 (i:2~10)的总和,为(n-1)*(n-1)/2
最优情况下交换0次,最差情况下交换n-1次
时间复杂度:O(n^2)
因为最坏只要交换n-1次所以选择排序普遍优于冒泡
直接插入排序
#include <stdio.h>
#include <stdlib.h>
#define M 10
void swap(int a[],int i,int j)
{
int t = a[i];
a[i] = a[j];
a[j] = t;
}
int main()
{
int num[M] = {9,1,5,8,3,7,4,6,10,2};
int k,j;
for(int i = 1; i < M; ++i)
{
k = num[i];
j = i;
while(j > 0 && num[j-1] > k)
{
num[j] = num[j-1];
j--;
}
num[j] = k;
}
for(int i = 0; i < M; ++i)
printf("%d ",num[i]);
return 0;
}
算法复杂度
最优(本身有序):比较n-1次
最差:外层循坏n-1次,内层比较 i 次——O(n^2)
希尔排序
希尔排序通过一个增量将原来的数组变成一个较为有序的序列,然后再执行一次增量为1的排序(等同于最普通的插入排序)。
每次内层循坏可以看做一个根据增量的分组,再对这个分组进行插入排序。
关于增量的选择:只要最后增量能够达到1即可
(代码也表现插入排序j的两种不同写法,但原理都是相同)
#include <stdio.h>
#include <stdlib.h>
#define M 10
void swap(int a[],int i,int j)
{
int t = a[i];
a[i] = a[j];
a[j] = t;
}
int main()
{
int num[M] = {9,1,5,8,3,7,4,6,2,10};
int increment = M;
int j,k;
do
{
increment = increment/3+1;
for(int i = increment; i < M; ++i)
{
if(num[i] < num[i-increment])
{
k = num[i];
for(j = i-increment; j >= 0 && num[j] > k; j -= increment) //根据增量分组出一个序列并对这个序列进行插入排序
num[j+increment] = num[j];
num[j+increment] = k;
}
}
}while(increment > 1);
for(int i = 0; i < M; ++i)
printf("%d ",num[i]);
return 0;
}
时间复杂度
O(nlogn)
堆排序
为什么用完全二叉树?因为这样第一次排序时,子结点只有一层,不用考虑忽略了最大结点在另一边的情况,底部排序好顶部的多层排序就足以完成。
这部代码充分体现了这一点。
void HeadSort(int num[],int m)
{
for(int i = m/2-1; i >= 0; --i)
HeapAdjust(num,i,m); //先维护底部
for(int i = 0; i < m-1; ++i)
{
swap(num,0,m-1-i);
HeapAdjust(num,0,m-i-1); 正如我所说的底部的有序使顶部的排序成为可能
}
}
为什么是选择排序的改进?因为它维护堆的方式,犹如选择排序,找出最大(或小)的子节点,保存下来最后互换两个结点。
#include <stdio.h>
#include <stdlib.h>
#define M 10
void swap(int a[],int i,int j)
{
int t = a[i];
a[i] = a[j];
a[j] = t;
}
void HeapAdjust(int num[],int s,int m) //将s-m维护成大顶堆
{
int t = num[s];
int j;
for(j = s*2+1; j < m; j = j*2+1)
{
if(j < m-1 && num[j] < num[j+1]) //防止越界与找出左右孩子中的最大值
j++;
if(t >= num[j])
break;
num[s] = num[j];
s = j; //代表着s与j的互换在下个循坏中起效
}
num[s] = t;
}
void HeadSort(int num[],int m)
{
for(int i = m/2-1; i >= 0; --i)
HeapAdjust(num,i,m);
for(int i = 0; i < m-1; ++i)
{
swap(num,0,m-1-i);
HeapAdjust(num,0,m-i-1); 正如我所说的底部的有序使顶部的排序成为可能
}
}
int main()
{
int num[M] = {9,1,5,8,3,7,4,6,2,10};
HeadSort(num,M);
for(int i = 0; i < M; ++i)
printf("%d ",num[i]);
return 0;
}
时间复杂度
构建堆:至多进行两次比较(由于底层已经构建完成,完全二叉树第一次构建底部只有一层子节点比较一次),时间复杂度O(n)
重建堆:对于第i次重建堆需要循坏log(M-i-1),且共进行M-1次——O(nlogn)
总体:O(nlogn)
归并排序
注意MSort会用到边界所以t一定要代入最后一个元素位置而不是数组大小(本代码中t要代入9不能是10)
递归实现
#include <stdio.h>
#include <stdlib.h>
#define MAXSIZE 10
void MergeSort(int a[],int n)
{
MSort(a,a,0,n-1);
}
void MSort(int SR[],int TR1[],int s,int t)
{
int TR2[MAXSIZE];
int m;
if(s == t)
TR1[s] = SR[s];
else
{
m = (s+t)/2;
MSort(SR,TR2,s,m);
MSort(SR,TR2,m+1,t);
Merge(TR2,TR1,s,m,t);
}
}
void Merge(int SR[],int TR[],int i,int m,int n)
{
int j,k,l;
for(k = i,j = m+1; i <= m && j <= n; ++k)
{
if(SR[i] > SR[j])
TR[k] = SR[j++];
else
TR[k] = SR[i++];
}
if(i <= m)
{
for(l = 0; l <= m-i; ++l)
TR[k+l] = SR[i+l];
}
else if(j <= n)
{
for(l = 0; l <= n-j; ++l)
TR[k+l] = SR[j+l];
}
}
int main()
{
int num[10] = {9,1,5,8,3,7,4,6,2,10};
MergeSort(num,10);
for(int i = 0; i < 10; ++i)
printf("%d ",num[i]);
return 0;
}
非递归实现
#include <stdio.h>
#include <stdlib.h>
#define MAXSIZE 10
void Merge(int SR[],int TR[],int i,int m,int t) //t为最尾元素下标
{
int j,k,l;
for(k = i,j = m+1; i <= m && j <= t; ++k)
{
if(SR[i] > SR[j])
TR[k] = SR[j++];
else
TR[k] = SR[i++];
}
if(i <= m)
{
for(l = 0; l <= m-i; ++l)
TR[k+l] = SR[i+l];
}
else if(j <= t)
{
for(l = 0; l <= t-j; ++j)
TR[k+l] = SR[j+l];
}
}
void MergePass(int SR[],int TR[],int s,int n) //注意n为最尾元素下标
{
int i = 0;
while(i <= n-2*s+1) //能否完成下次归并的最小元素个数
{
Merge(SR,TR,i,i+s-1,i+2*s-1);
i = i+2*s;
}
//将剩余的序列归并
if(i < n-s+1) //若有超过s长度的序列则可以归并
Merge(SR,TR,i,i+s-1,n);
else
{
for(int j = i; j <= n; ++j)
TR[j] = SR[j];
}
}
void MergeSort(int TR[],int n) //n为数组长度
{
int *SR = (int *)malloc(sizeof(int)*n);
int k = 1;
while(k < n)
{
MergePass(TR,SR,k,n-1);
k *= 2;
MergePass(SR,TR,k,n-1);
k *= 2;
}
}
int main()
{
int num[MAXSIZE] = {9,1,5,8,3,7,4,6,2,10};
MergeSort(num,MAXSIZE);
for(int i = 0; i < 10; ++i)
printf("%d ",num[i]);
return 0;
}
时间复杂度
O(nlogn)
快速排序
#include <stdio.h>
#include <stdlib.h>
void q_sort(int num[],int begin,int end)
{
if(begin >= end)
return;
int t = num[begin];
int i = begin, j = end-1;
while(i < j)
{
while(i < j && num[j] >= t)
j--;
if(i < j)
{
num[i] = num[j];
i++;
}
while(i < j && num[i] <= t)
i++;
if(i<j)
{
num[j] = num[i];
j--;
}
}
num[i] = t;
q_sort(num,begin,i-1);
q_sort(num,i+1,end);
}
int main()
{
int num[10] = {9,1,5,8,3,7,4,6,2,10};
q_sort(num,0,10);
for(int i = 0; i < 10; ++i)
printf("%d ",num[i]);
return 0;
}
快排优化
#include <stdio.h>
#include <stdlib.h>
#define MAX_LENGTH_INSERT_SORT 7
void InsertSort(int num[],int n)
{
int t;
int j;
for(int i = 1; i < n; ++i)
{
t = num[i];
j = i;
while(j > 0 && num[j-1] > t)
{
num[j] = num[j-1];
j--;
}
num[j] = t;
/*
for(j = i-1; j >= 0 && num[j] > t; --j)
num[j+1] = num[j];
num[j+1] = t;
*/
}
}
int Partitionl(int a[],int low,int high)
{
int i = low, j = high;
int t = a[i];
while(i < j)
{
while(i < j && a[j] >= t)
j--;
if(i < j)
{
a[i] = a[j];
i++;
}
while(i < j && a[i] <= t)
i++;
if(i < j)
{
a[j] = a[i];
j--;
}
}
a[i] = t;
return i;
}
void QSort(int a[],int low,int high,int n)
{
int p;
if((high-low) > MAX_LENGTH_INSERT_SORT)
{
while(low < high)
{
p = Partitionl(a,low,high);
QSort(a,low,p-1,n);
low = p+1;
}
}
else
InsertSort(a,n);
}
int main()
{
int num[10] = {9,1,5,8,3,7,4,6,2,10};
QSort(num,0,9,10);
for(int i = 0; i < 10; ++i)
printf("%d ",num[i]);
return 0;
}
时间复杂度
O(logn)