排序算法-选择类排序

来源:互联网 发布:手机迅雷提示无网络 编辑:程序博客网 时间:2024/05/21 01:55

选择排序的算法思想:

重待排序的元素序列中选择最小(最大)的元素,将其放入在已排序序列的最前(最末),其余的元素构成新的待排序列。依次类推,直到待排序元素序列中没有待排元素。选择排序主要有两种:简单选择排序和堆排序。接下来我们来分别介绍一下这两种排序算法。

一、选择排序

算法思想:

简单选择排序是一种简单的选择类排序算法,它是通过依次找到待排序列中最小的数据元素,并将其放在序列的最前面,从而使待排序元素变成有序序列。它的基本思想描述如下:假设待排序的元素序列有n个元素,在第一趟排序过程中,从n个元素序列中选择最小的元素,并将其放在元素序列的最前面即第一个位置。第二趟排序过程中,从剩余的n-1个元素中,选择最小的元素,将其放在第二个位置。依次类推,直到没有带比较的元素,简单选择排序算法结束。例如,给定一个待排序序列:55 33 22 66 44     过程如下:第一个元素和其后的4个元素依次比较,55>33,33较小元素,33>22,22较小元素,22<66,22<44,较小元素任为22,最后将22与第一个元素交换,第一趟排完:22 33 55 66 44。33分别与55、66、44比较,33最小不用交换,第二趟排完:22 33 55 66 44,依次类推,第三趟排完:22 33 44 66 55,第四趟排完:22 33 44 55 66,前4个元素全部有序,第5个元素是最大的元素,排在第五个位置上自然也是有序,排序结束。

示例:65 32 71 28 83 7 53 49
第一趟:{7} 32 71 28 83 65 53 49
第二趟:{7 28} 71 32 83 65 53 49
第三趟:{7 28 32} 71 83 65 53 49
第四趟:{7 28 32 49} 83 65 53 71
第五趟:{7 28 32 49 53} 65 83 71
第六趟:{7 28 32 49 53 65} 83 71
第七趟:{7 28 32 49 53 65 71} 83

选择排序算法的实现:

void SelectSort(int a[],int n){    /*选择排序*/    int i,j,k,t;    /*将i个元素和i+1...n个元素比较,将最小元素放在i个位置上*/    for(i=0;i<n-1;i++){        j=i;        for(k=i+1;k<n;k++){/*序号最小的元素为j*/            if(a[k]<a[j])                j=k;        }        /*如果i不等于j,则需要交换a[i]和a[j]*/         if(j!=i) {            t=a[i];a[i]=a[j];a[j]=t;        }    } }

主函数:

#include<stdio.h>void SelectSort(int a[],int n);/*简单选择排序*/ void DispArray(int a[],int n);/*输出数组序列*/void HeapSort(int a[],int n);/*堆排序*/int main(){    int a[]={65,32,71,28,83,7,53,49};    int n=8;    SelectSort(a,n);    //HeapSort(a,n);    DispArray(a,n);    return 0;} 

输出数组函数:

void DispArray(int a[],int n){    int i;    for(i=0;i<n;i++)        printf("%d ",a[i]);    printf("\n");} 

主要用途:

简单选择排序算法实现简单,适用于待排序元素较少且对时间要求不高的场合。

稳定性与复杂度:

简单选择排序不是一种稳定的排序算法。在最好的情况下,待排序元素序列暗中非递减排列,则不需要移动元素。在最坏的情况下,待排序按照非递增排列,每一趟排序都需要移动元素,移动次数为3(n-1)。在任何情况下,简单选择排序算法都需要n*(n-1)/2次比较,总上所述,简单选择排序算法时间复杂度为O(n2)。空间复杂度为O(1)。

二、堆排序

堆排序也是属于选择类排序,它是简单选择排序算法的改进。堆排序利用二叉树的性质对元素进行排序,将完全二叉树从上到下、从左到右依次编号,如果每一个双亲节点元素值大于(小)等于该孩子节点的元素值,则根据编号构成的元素序列就是一个大(小)顶堆。

算法思想:

假设一个大顶堆中有n个元素,如果将堆中的根节点元素输出之后,再将剩下n-1个元素重新建立成一个新堆,并将根节点元素输出,然后将剩下的n-2个元素重新建立成堆,重复上述过程,知道堆中没有元素为止。输出的元素就是一个有序序列,这样的排序方法称为堆。

1、创建堆

假设待排序元素有n个 ,依次放在数组a中,第1个元素的关键字a[1]表示二叉树的根节点,剩下的元素a[2...n]依次与完全二叉树中的编号一一对应。例如,a[1]的左孩子节点元素放在a[2]中,右孩子节点元素放在a[3]中,a[i]的左孩子放在a[2*i]中,右孩子放在a[2*i+1]中。如果是大顶堆,则有a[i]>=a[2*i]且a[i]>=a[2*i+1] (i=1,2,...|n/2|向下取整)。建立大顶堆的算法思想:从位于元素序列中最后一个非叶子结点(第|n/2|元素)开始,逐层比较调整元素的位置使其满足a[i]>=a[2*i]且a[i]>=a[2*i+1] ,直至根节点为止。假设当前节点序号为i,则当前元素为a[i],其左孩子为a[2*i]右孩子a[2*i+1]。将左孩子为a[2*i]右孩子a[2*i+1]与a[i]比较,如果孩子节点元素值大于当前节点值,则交换两者;否则不交换。逐层向上执行此操作,直至根节点,这样就建立大顶堆。

i: 1 2 3 4 5 6 7 8
a[i]: 27 58 42 53 42 69 50 62
(1)初始状态
(2)从第4个元素开始,53<62,交换两个节点元素。
(3)比较第3个元素与其子树节点,因为69>50且42<69,所以交换42和69。
(4)比较第2个元素与其子树节点,因为62>42且62<58,所以交换58和62。
(5)比较第1个元素与其子树节点,因为69>62且27<69,所以交换27和69。
(6)比较第3个元素与其子树节点,因为50>42且27<50,所以交换27和50。

void CreateHeap(int a[],int n){    /*建立大顶堆*/    int i;    for(i=n/2-1;i>=0;i--)/*从n/2-1开始建立*/         AdjustHeap(a,i,n-1);}

2、调整堆

调整堆算法描述:输出堆顶元素可以将堆顶元素放在堆的最后面,即将第1个元素与最后一个元素交换,则需要调整的元素序列就是a[1...n-1]。从根节点开始,如果其左、右节点元素值大于根节点元素值,选择较大的一个进行交换,也就是说如果a[2]>a[3],则将a[1]与a[2]惊喜交换,否则不交换。如果a[3]>a[2],则将a[1]与a[3]惊喜交换,否则不交换。逐层重复执行此操作,直至叶子节点,就完成了堆的调整,构成了一个新堆。
void AdjustHeap(int a[],int s,int m){    /*调整a[s...m],使其成为一个大顶堆*/     int t,j;    t=a[s]; /*将根节点暂时保存在t中*/     for(j=2*s+1;j<=m;j*=2+1){        if(j<m&&a[j]<a[j+1])/*沿关键字较大的孩子节点向下筛选*/             j++;   /*j为关键字较大的节点的下标*/             if(t>a[j])                break;            a[s]=a[j];            s=j;    }    a[s]=t;/*将根节点插入到正确的位置*/ }

堆排序:

void HeapSort(int a[],int n){    /*利用堆排序算法对数组a中的元素进行排序*/    int t,i;    CreateHeap(a,n);    for(i=n-1;i>0;i--){        /*将堆顶元素与最后一个元素交换,从新调整堆*/        t=a[0];        a[0]=a[i];        a[i]=t;        AdjustHeap(a,0,i-1);/*将a[0...i-1]调整为大顶堆*/     } } 

主要用途:

堆排序算法实现比较复杂,它主要适用于大规模的数据排序,例如,如果需要在10万个数据元素找出前10个最小元素或最大元素,则适用堆排序算法效率最高。

稳定性和复杂度:

堆排序是不稳定排序算法。堆排序的时间耗费主要是在建立堆和调整堆时。一个深度h,元素个数为n的堆,其调整算法的比较次数最多是2(h-1)次,建立一个堆最多比较次数4n。一个完整的堆排序过程总共比较次数少于2nlog2n,依次堆排序平均时间复杂度和最坏时间复杂度都是O(nlog2n)。堆排序空间复杂度为O(1)。
1 0