直接选择排序:
这是一年前的选择排序:
http://write.blog.youkuaiyun.com/postedit/51406950.
思想:
假设第一个数最大,然后让这个数与后面其他的数依次进行比较,假如第一个位置上的数小的话,就交换位置,这样第一个位置上的数一直存放的每一趟比较完之后最大的数。
缺点:
1.每次交换位置会浪费很多时间
2. 对于n个键值选出最小,至少要进行n-1比较,然而继续在剩余的n-1个数中找出最小的,要进行n-2次比较,比较次数过多,时间复杂度过大,为O(n^2).
优化一:
每一趟比较只是标记出数值较大的数的位置,当一趟比较完成之后找到最大的数的时候再交换位置来减少交换次数。
代码:
void SelectSort(List R,int n)
{
int m,i,j;
//第一轮,从第一个数开始比较
for(i=1;i<=n-1;i++)
{
min=i;
for(j=i+1,j<=n,j++)
if(R[min].key>R[j].key)
min=j;//数组下标为min的里面存放的是最小的数
if(min!=i) swap(R[min].R[i]);//将最小值记录与第i个数进行交换
}
}
优化二:
每一趟比较都利用前一趟的比较结果,可以大大减少比较的次数,也就是利用堆排序。
堆排序:以建最小堆为例
关键的两步:
1.把初始序列建成一个堆
假设有一个无序序列R[n],最后一个非终端结点为R[n/2], 实际是 从最后一个非终端结点开始逐渐建堆的过程,从R[n/2]开始,逐步把以R[n/2], R[n/2-1],R[n/2-2]……R[1]为根的子树筛选成堆。其实就是第二部的一个不断反复的过程。
2.在输出堆顶元素之后,调整剩余的元素成为一个堆
以下面这个堆为例,在输出13之后,让最后一个结点的值放在堆顶,这样椭圆里面的仍然是一个堆
把堆顶元素和两个孩子结点进行比较,选出一个小的值与堆顶元素进行交互,这样堆顶元素为最小的值。即27与92进行交换,27成为根结点,根结左侧没有进行任何处理,所以仍然为一个最小堆。
根的右侧进行了互换,所以不一定是最小堆,这时让根结点92和他的孩子结点进行比较,选出小的与92进行互换,所以称为下图这样,92已成为一个最小堆,这时整个序列为最小堆。
代码:
1.把堆顶元素输出之后,剩余的元素重新建堆
void Sift(List R,int k,int m)
{
int i,j,x;
List t;
i=k;j=2*i;
x=R[k].key;
t=R[k];
while(j<=m)
{
if(j<m)&& (R[j].key>R[j+1].key))
j++;
if(x<R[j].key)break;
else
{
R[i]=R[j];
i=j;
j=2*i;
}
}
R[i]=t;
}
2.堆排序
void HeapSort(List R)
{
int i;
for(i=n/2;i>=1;i--)
Sift(R,i,n); //从第n/2个元素开始进行筛选建堆
for(i=n;i>=2;i--)
{
swap(R[1],R[i]); //将堆顶记录和堆中的最后一个记录交换
Sift(R,1,i-1); //调整剩下的元素为堆
}
}
总结:
堆排序很明显利用上一次的排序结果每次减少了比较的次数,这样使得时间复杂度大大减小,它的时间复杂度为O(n log2 n).