排序算法选择排序——简单选择排序、堆排序代码(C/C++)

选择排序的思想:每一趟(如第i趟)在后面的n-i+1(i=1,2,3…n-1)个待排序元素中选取关键字最小的元素,作为有序子序列的第i个元素,直到n-1趟做完,待排序元素就剩下1个,就不用再选了。
选择排序主要有两种:

1.简单选择排序

简单选择排序每趟选取最小(最大)的一个元素放在有序子序列中,因此,n个元素的数组需要n-1趟排序才能使整个排序表有序。

void SelectSort(int *a,int len){
    for(int i=0;i<len-1;i++){
        int minindex=i;
        for(int j=i+1;j<len;j++){
            if(a[j]<a[minindex])
                minindex=j;
        }
        if(minindex!=i){
            int temp=a[i];
            a[i]=a[minindex];
            a[minindex]=temp;
        }
    }
}

性能分析
空间效率:O(1)
时间效率:简单选择排序中关键字比较次数与序列初始状态无关,始终是n(n-1)/2,时间复杂度始终是O(n^2)
稳定性:不稳定,如 {2,2 *,1},排序后{1,2 *,2}

2.堆排序

堆的定义:n个关键字的序列L[1…n],当且仅当满足下列条件时:

1)满足 L[i] >= L[2i] 且L[i]>=L[2i+1] ,称之为大根堆,或
2)满足 L[i] <= L[2i] 且L[i]<=L[2i+1] ,称之为小根堆。
堆的定义可以将其看做是一颗完全二叉树,则当下标为i的结点的值均大于其左孩子(下标为2i)及右孩子(2i+1)时,称为大根堆,反之为小根堆。
注:由于堆排序中需要比较下标为 i 结点与其左右孩子结点的值,因此我们习惯在堆排序时将数组定义为L[1…n],空出L[0]位置,因此其中下标为i的结点的左右孩子下标正好是2i和2i+1。另外,下标为 i 的结点,其双亲结点的下标为i/2向下取整

堆排序的思想(以大根堆为例,采用大根堆的堆排序结果为升序,小根堆结果为逆序):

1.首先将L[1…n]的n个元素建立为大根堆,不难看出,在大根堆中,最大的元素在堆顶位置
2.输出堆顶元素即为最大值放在表尾,并将该元素从堆中删除
3.将去掉堆顶后剩下的元素重新调整为堆,重复以上,直到剩下最后一个元素为止。

#include<iostream>
#include<stdlib.h>
using namespace std;

//从下标为k的结点开始向下调整,使之成为大根堆
void HeadAdjust(int *a,int k,int len){
    a[0]=a[k];//将该元素保存在a[0],防止覆盖
    for(int i=2*k;i<=len;i*=2){
        if(i<len&&a[i]<a[i+1])
            i++;       //比较左右孩子结点的大小,让i指向左右孩子中较大的一个
        if(a[0]>a[i])  //如果该元素已经大于左右孩子中较大的时,表明以该节点为根的堆已经为大根堆
            break;
        else{  
            a[k]=a[i];
            k=i;//接着向下调整
        }
    }
    a[k]=a[0];
}
//建立初试大根堆
//建堆时,对于L【1..n】构成的完全二叉树,其下标n/2的元素正好为第一个左右孩子不为空的结点,
//我们只需从n/2开始向前依次调整
void BuildMaxHeap(int *a,int len){
    for(int i=len/2;i>0;i--){ 
        HeadAdjust(a,i,len);
    }
}
//堆排序
//第一步:建立初始大根堆;
//第二步:交换堆顶与表尾元素(即删除堆顶元素),再接着进行n-1趟向下调整输出
void HeapSort(int *a,int len){
    BuildMaxHeap(a,len);//建立大根堆
    for(int i=len;i>1;i--){
        int temp=a[i];//将建立好的大根堆的堆顶元素(最大元素)移到表尾,与表尾交换
        a[i]=a[1];
        a[1]=temp;
        HeadAdjust(a,1,i-1);//再将剩下的元素重新调整为堆
    }
}

int main(){
    int a[]={0,3,1,4,6,9,5412,4,5,72,3,512,35,7421,3,5,42,2,44,1,23,54,1,2,66,523,565,2};
    a[0]=0;//将第一个位置空下来
    int len=sizeof(a)/sizeof(int)-1;//计算数组长度
    for(int i=1;i<=len;i++){
        cout<<a[i]<<" ";
    }
    cout<<endl;
    HeapSort(a,len);//堆排序
    for(int i=1;i<=len;i++){
        cout<<a[i]<<" ";
    }
    return 0;
}
性能分析

空间效率:O(1)
时间效率:建立初始堆时间为O(n),之后有n-1趟向下调整操作,每次调整的时间复杂度为O(h)=O(log2n)(h表示n个元素列表构成的完全二叉树的高度),故在最好、最坏、平均情况下,堆排序时间复杂度为建堆时间+向下调整时间O(n)+O((n-1)*h)=O(n)+O((n-1)*log2n)=O(nlog2n)
稳定性:不稳定

堆排序适合关键字较多的情况。如从1亿个数中选取前100个最大值,首先使用一个大小为100的数组,读入前100个数,建立小根堆,而后依次读入剩下的数,若小于堆顶元素,则舍弃,若大于堆顶,则用该数替代堆顶并重新调整堆,带数据读取完毕,堆中的100个数即为所求。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值