第二类的排序,选择排序,有两种,一种是直接选择排序,一种是堆排序,
堆排序用到了树部分的知识,下面来看这2种排序。
第一种为选择排序:
基本思想:
第i趟排序开始时,当前有序区和无序区分别为R[0..i-1]和R[i..n-1](0≤i<n-1),
该趟排序则是从当前无序区中选出关键字最小的记录R[k],将它与无序区的第1个记录R[i]交换,
使R[0..i]和R[i+1..n-1]分别变为新的有序区和新的无序区。
因为每趟排序均使有序区中增加了一个记录,且有序区中的记录关键字均不大于无序区中记录的关键字,
即第i趟排序之后R[0..i]的所有关键字小于等于R[i+1..n-1]中的所有关键字,所以进行n-1趟排序之后
有R[0..n-2] 的所有关键字小于等于R[n-1].key,即经过n-1趟排序之后,整个表R[0..n-1]递增有序。
代码如下:
void SelectSort(RecType R[],int n)
{ int i,j,k; RecType temp;
for (i=0;i<n-1;i++) /*做第i趟排序*/
{ k=i;
for (j=i+1;j<n;j++) /*在[i..n-1]中选key最小的R[k] */
if (R[j].key<R[k].key)
k=j; /*k记下的最小关键字所在的位置*/
if (k!=i) /*交换R[i]和R[k] */
{ temp=R[i]; R[i]=R[k]; R[k]=temp; }
}
}
堆排序:将无序序列建成一个堆,得到关键字最小(或最大)的记录;
输出堆顶的最小(大)值后,使剩余的n-1个元素重又建成一个堆,
则可得到n个元素的次小值;重复执行,得到一个有序序列,
堆排序需解决的两个问题
如何由一个无序序列建成一个堆?
如何在输出堆顶元素之后,调整剩余元素,使之成为一个新的堆?
第二个问题解决方法——筛选
方法:输出堆顶元素之后,以堆中最后一个元素替代之;然后将根结点值
与左、右子树的根结点值进行比较,并与其中小者进行交换;重复上述操作,
直至叶子结点,将得到新的堆,称这个从堆顶至叶子的调整过程为“筛选”
三个步骤如下:
建立一个初堆:
void crt_heap(RecordType r[], int length )
/*对记录数组r建堆,length为数组的长度*/
{
n= length;
for ( i=n/2 ; i>= 1 ; --i) /* 自第个记录开始进行筛选建堆 */
sift(r,i,n) ;
}
重建序列的算法为:
void sift(RecType R[],int low,int high)
{ int i=low,j=2*i; /*R[j]是R[i]的左孩子*/
RecType temp=R[i];
while (j<=high)
{ if (j<high && R[j].key<R[j+1].key) j++;
if (temp.key<R[j].key)
{ R[i]=R[j]; /*将R[j]调整到双亲结点位置上*/
i=j; /*修改i和j值,以便继续向下筛选*/
j=2*i;
}
else break; /*筛选结束*/
}
R[i]=temp; /*被筛选结点的值放入最终位置*/
}
排序的算法为:
void HeapSort(RecType R[],int n)
{ int i; RecType temp;
for (i=n/2;i>=1;i--) /*循环建立初始堆*/
sift(R,i,n);
for (i=n;i>=2;i--) /*进行n-1次循环,完成推排序*/
{ temp=R[1]; /*将第一个元素同当前区间内R[1]对换*/
R[1]=R[i];R[i]=temp;
sift(R,1,i-1); /*筛选R[1]结点,得到i-1个结点的堆*/
}
}