一、基本思想
选择排序(Selection Sort)的基本思想是:不断从待排记录序列中选出关键字最小的记录插入已排序记录序列的后面,直到n个记录全部插入已排序记录序列中。
1、简单选择排序
第i趟排序开始时,有序区和无序区分别为R[1..i-1]和R[i..n](i=1,2,..n-1),该趟排序则是从n-i+1个记录选取关键字最小的记录R[k],并与第i(i=1,2,...,n)个记录交换,形成新的有序区和无序区。从i=1开始,每次从剩余元素中选出最小(最大)元素。这就是直接选择排序。
2、树型选择排序
又称为锦标赛排序,是一种按锦标赛思想进行选择排序的方法。先对n个记录的关键字进行两两比较,然后在其中的ceil(n/2)个较小者间再进行两两比较,如此往复,直至选出最小关键字的记录为止。这个过程可用一棵n个结点的完全二叉树来表示。如图所示:
但是这种方法存在辅助空间较多,“最大值”进行多余比较等缺点,J.willioms在1964提出了堆排序。
3、堆排序
(1)基本概念
a)堆:设有n个元素的序列:
{k1, k2, ..., kn}
对所有的i=1,2,...,(int)(n/2),当满足下面关系:
ki≤k2i,ki≤k2i+1
或 ki≥k2i,ki≥k2i+1
这样的序列称为堆。
堆的两种类型:
根结点最小的堆----小根堆。
根结点最大的堆----大根堆。
根结点称为堆顶,即:在一棵完全二叉树中,所有非叶结点的值均小于(或均大于)左、右孩子的值。
b)堆排序:是一种树型选择排序,特点是,在排序过程中,把R[1..n]看成是一个完全二叉树的存储结构,利用完全二叉树双亲结点和孩子结点的内在关系,在当前无序区中选择关键字最大(最小)的记录。
(2)堆排序步骤:
1、从k-1层的最右非叶结点开始,使关键字值大(或小)的记录逐步向二叉树的上层移动,最大(或小)关键字记录成为树的根结点,使其成为堆。
2、逐步输出根结点,令r[1]=r[i](i=n,,n-1,...,2),在将剩余结点调整成堆。直到输出所有结点。我们称这个自堆顶到叶子的调整过程为“筛选”。
(3)要解决的两个问题:
1、如何由一个无序序列建成一个堆;
2、输出一个根结点后,如何将剩余元素调整成一个堆。
将一个无序序列建成一个堆是一个反复“筛选”的过程。若将此序列看成是一个完全二叉树,则最后一个非终端结点是第floor(n/2)个元素,由此“筛选”只需从第floor(n/2)个元素开始。
堆排序中需一个记录大小的辅助空间,每个待排的记录仅占有一个存储空间。堆排序方法当记录较少时,不值得提倡。当n很大时,效率高。堆排序是非稳定的。
堆排序的算法和筛选的算法如第二节所示。为使排序结果是非递减有序排列,我们在排序算法中先建一个“大顶堆”,即先选得一个关键字为最大的记录并与序列中最后一个记录交换,然后对序列中前n-1个记录进行筛选,重新将它调整为一个“大顶堆”,然后将选得的一个关键字为最大的记录(也就是第一个元素)与当前最后一个记录交换(全局看是第n-1个),如此往复,直到排序结束。由到,筛选应按关键字较大的孩子结点向下进行。
二、算法的C语言描述
1、简单选择排序
void SelectSort(SeqList &L);
{int i,j,k;
for(i=1;i<=L.lenght;i++)
{ k=i;
for(j=i+1;j<=L.lenght;j++)
if(R[j].key<R[k].key)k=j;
if(k!=i) { R[0]=R[i];R[i]=R[k];R[k]=R[0];}
}
}
2、树型选择排序
略(实践中几乎不用)
三、算法的C语言实现
#include"stdio.h"
#include"stdlib.h"
#define OK 1
#define MAXSIZE 20
typedef intKeyType;
typedef intStatus;
typedef struct
{
KeyType key;
//InfoTypeotherinfo;
}RedType;
typedef struct
{
RedTyper[MAXSIZE+1];
int length;
}Sqlist;
typedef SqlistHeapType;
voidHeapAdjust(HeapType &H,int s,int m)
{
RedType rc;
rc=H.r[s];
for(intj=2*s;j<=m;j*=2)
{
if(j<m&&(H.r[j].key<H.r[j+1].key))++j;
if(rc.key>=H.r[j].key) break;
H.r[s]=H.r[j];
s=j;
}//for
H.r[s]=rc;
}//HeapAdjust
voidHeapSort(HeapType &H)
{
for(inti=H.length/2;i>0;--i)
HeapAdjust(H,i,H.length);
for(inti=H.length;i>1;--i)
{
H.r[0]=H.r[1];
H.r[1]=H.r[i];
H.r[i]=H.r[0];
HeapAdjust(H,1,i-1);
}//for
}//HeapSort
void InputL(Sqlist&L)
{
printf("inputthe length:\n");
scanf("%d",&L.length);
printf("inputthe data needed to sort:\n");
for(inti=1;i<=L.length;i++)
scanf("%d",&L.r[i].key);
}//InputL
voidOutputL(Sqlist &L)
{
printf("thedata after sorting is:\n");
for(inti=1;i<=L.length;i++)
printf("%d ",L.r[i].key);
}
int main()
{
Sqlist H;
InputL(H);
HeapSort(H);
OutputL(H);
return OK;
}
四、堆排序示例及其复杂度
对深度为k的堆,筛选算法中进行关键字的比较次数至多为2(k-1)。建堆时,n个元素所需的比较次数不超过4n。堆排序时,<2nlogn。堆排序在最坏的情况下,时间复杂度为O(n㏒n)。