排序算法-选择类排序

选择排序的算法思想:

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

一、选择排序

算法思想:

简单选择排序是一种简单的选择类排序算法,它是通过依次找到待排序列中最小的数据元素,并将其放在序列的最前面,从而使待排序元素变成有序序列。它的基本思想描述如下:
假设待排序的元素序列有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
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值