1.插入排序:直接插入排序,Shell排序
2.选择排序:直接选择排序,堆排序
3.交换排序:冒泡排序,快速排序
4.归并排序
时间代价:比较次数、移动次数
空间代价:额外空间、堆栈深度
稳定性:存在多个具有相同排序码的记录排序后这些记录的相对次序保持不变
算法思想:以第一个元素为准,小于该元素的放在左边,不小于该元素的放在右边,然后对两侧元素递归排序。
算法:
void quicksort(int l, int u)
{ int i, m;
if (l >= u) return;
m = l;
for (i = l+1; i <= u; i++)
if (x[i] < x[l])
swap(++m, i);
swap(l, m);
quicksort(l, m-1);
quicksort(m+1, u);
}
这里假设x为全局变量。
随机化改进:不是选取第一个值为基准,而是随机选取。
平衡化改进:取第一个、最后一个和中间点三个值中中间值为基准进行排序。
设置阀值--混合排序:当数组长度小于某一值时使用其他较快的排序。
时间代价:最好情况是O(n log n),最坏情况是O(n2)。如果设f(n)为数组长为n时的比较次数,则f(n)=[(f(1)+f(n-1))+(f(2)+f(n-2))+...+(f(n-1)+f(1))]/n.
利用数学知识易知f(n)=(n+1)*[1/2+1/3+...+1/(n+1)]-2n~1.386nlog(n).
空间代价:程序所需的空间即为堆栈深度(用于存储l,u,m),所以空间代价为O(log(n))
稳定性:快速排序时不稳定的,即不保序的。
算法思想:将长为的n序列分为长度相当的左右两列,分别排序,然后再合并。即先分后合。
算法:
void merge_sort(int l,int u)
{
if(l+1>=u){basic_merge_sort(l,u);return;}
int c=(l+u)/2;
merge_sort(l,c);
merge_sort(++c,u);
merge(l,u);
}
其中basic_nerge_sort算法为:
void basic_merge_sort(int l,int u)
{
if((u-l==1)&&(x[l]>x[u]))
swap(l,u);
}
其中的merge算法作用是:将两个有序的序列排成一个有序序列,算法如下:
void merge(int l,int u)
{
int c=(l+u)/2,j=c+1,i;
for(i=l;i<=u;i++)
y[i]=x[i];
i=l;
while(l<=c&&j<=u)
{
if(y[l]>y[j]) x[i++]=y[j++];
else x[i++]=y[l++];
}
while(l<=c) x[i++]=y[l++];
while(j<=u) x[i++]=y[j++];
}
改进:归并排序使用时基本上使用的和这类似。
算法分析:
时间代价:设f(n)为数组长为n时的比较次数,则f(n)=f(n/2)+f((n+1)/2)+n.则利用数学知识很容易看出f(n)为O(nlog(n))的。
空间代价:归并排序所需空间除了堆栈深度以外还需要开长度为n的空间。所以归并排序的空间代价为O(n)。
稳定性:由于归并排序中并没有使用出现对换,所以排序时稳定的。
时间代价:从时间复杂度上看,两个算法平分秋色。但理论分析并不等于实际运行结果。于是我对两种算法用C实现了一下,分别用visual stdio C++6.0和Dev C++编译,在我的COMPAQ B1800笔记本(1.73GHz主频)上运行。运行结果如下:(N为数组长度,由于排序算法很快,且快排运行时间随机性比较大,我对每个排序都运行了times次,每次数组元素都是随机选取)
visual stdio C++6.0上运行时间(ms)
N和times 归并 快排
N=500 times=10000 1395 2593
N=1000 times=10000 3165 5645
N=2000 times=10000 6974 12115
N=10000 times=1000 4308 6986
N和times 归并 快排
N=500 times=10000 591 594
N=1000 times=10000 1515 907
N=2000 times=10000 2620 2381
N=10000 times=1000 3156 3172
归并排序堆栈深度为O(log(n)),但还需要额外的大小为n的空间,所以空间代价为O(n)。
从空间代价上来看,归并排序不如快速排序。