选择类排序

简单选择排序 
  简单选择排序(simple selection sort)也是直接选择排序。此方法在一些高级语言课程中做过介绍,是一种较为容易理解的方法。
1.算法思想
  对于一组关键字{K1,K2,…,Kn}, 首先从K1,K2,…,Kn中选择最小值,假如它是 Kz,则将Kz与 K1对换;然后从K2,K3,… ,Kn中选择最小值 Kz,再将Kz与K2对换。如此进行选择和调换n-2趟,第(n-1)趟,从Kn-1、Kn中选择最小值 Kz将Kz与Kn-1对换,最后剩下的就是该序列中的最大值,一个由小到大的有序序列就这样形成。
【例】图9.6是一个有5个关键字{3,4,1,5,2}的简单选择排序过程的示意图。
        
2.具体算法
选择法排序原理:一次选定数组中的每一个数,记下当前位置并假设它是从当前位置开始后面数中的最小数min=i,从这个数的下一个数开始扫描直到最后一个数,并记录下最小数的位置min,扫描结束后如果min不等于i,说明假设错误,则交换min与i位置上数。 具体实现代码如下:

#include<stdio.h>
/****************************************
**选择排序法对十个整数进行排序        ***
****************************************/
//n为数组长度;
void sort(int a[],int n)
{
   int temp,min;
   for(int i=0;i<n;i++)
   {
     min=i;//先假设最小下标为i
     for(int j=i+1;j<n;j++)
        if(a[j]<a[min])
           min=j;//对i之后的数进行扫描将最小的数赋予min
     if(min!=i)
     {
       temp=a[i];
       a[i]=a[min];
       a[min]=temp;
     }//判断min与i是否相等,若=则说明原假设正确反之交换数值
   }
}
//用main函数验证
void main()
{
 int a[10];
 printf("please input the array a:\n");
 for(int i=0;i<10;i++)
  scanf("%d",&a[i]);
 sort(a,10);

    for(i=0;i<10;i++)
  printf("%d\t",a[i]);
}


3.算法的时间复杂度

    该算法的时间复杂度为 O(n2)。并且排序是稳定的。



堆排序 
   1.定义

  树形选择排序(锦标赛排序),1964年威洛姆斯(J.Willioms)提出了进一步改正的排序方法,即堆排序(heap sort)。
堆是n个元素的有限序列{ K1,K2,…,Kn },它当且仅当满足如下关系:
              
  这是一个上小、底大的堆。若是一个上大、底小的堆,只需把“ <= ”改为“ >= ”即可。堆是一种数据元素之间的逻辑关系,常用向量做存储结构。对于满二叉树,当对它的结点由上而下,自左至右编号之后,编号为 i 的结点是编号为 2i 和 2i+1 结点的双亲。反过来讲,结点 2i 是结点 i 的左孩子,结点 2i+1 是结点 i 的右孩子。图 9.7 表示完全二叉树和它在向量中的存储状态。结点编号对应向量中的下标号。 
  用堆的概念分析向量中的数据,它显然满足(上小、底大)堆的关系。不难看出满足堆的逻辑关系的一组数据,可画成二叉树的形状,并且它是一棵完全二叉树树形。因此,也可借助完全二叉树来描述堆的概念。若完全二叉树中任一非叶子结点的值小于等于(或大于等于)其左、右孩子结点的值,则从根结点开始按结点编号排列所得的结点序列就是一个堆。在图 9.8 中 (a) 、 (c) 是堆, (b) 、 (d) 不是堆。

堆排序的算法思想
    堆排序利用了大根堆(或小根堆)堆顶记录的关键字最大(或最小)这一特征,使得在当前无序区中选取最大(或最小)关键字的记录变得简单。

(1)用大根堆排序的基本思想
  ① 先将初始文件R[1..n]建成一个大根堆,此堆为初始的无序区
  ② 再将关键字最大的记录R[1](即堆顶)和无序区的最后一个记录R[n]交换,由此得到新的无序区R[1..n-1]和有序区R[n],且满足R[1..n-1].keys≤R[n].key
  ③ 由于交换后新的根R[1]可能违反堆性质,故应将当前无序区R[1..n-1]调整为堆。然后再次将R[1..n-1]中关键字最大的记录R[1]和该区间的最后一个记录R[n-1]交换,由此得到新的无序区R[1..n-2]和有序区R[n-1..n],且仍满足关系R[1..n-2].keys≤R[n-1..n].keys,同样要将R[1..n-2]调整为堆。
    ……
直到无序区只有一个元素为止。

(2)大根堆排序算法的基本操作:
  ① 初始化操作:将R[1..n]构造为初始堆;
  ② 每一趟排序的基本操作:将当前无序区的堆顶记录R[1]和该区间的最后一个记录交换,然后将新的无序区调整为堆(亦称重建堆)。
  注意:
  ①只需做n-1趟排序,选出较大的n-1个关键字即可以使得文件递增有序。
  ②用小根堆排序与利用大根堆类似,只不过其排序结果是递减有序的。堆排序和直接选择排序相反:在任何时刻,堆排序中无序区总是在有序区之前,且有序区是在原向量的尾部由后往前逐步扩大至整个向量为止。


3.具体算法

 // array是待调整的堆数组,i是待调整的数组元素的位置,length是数组的长度
void HeapAdjust(int array[], int i, int nLength)
{
    int nChild, nTemp;
    for (nTemp = array[i]; 2 * i + 1 < nLength; i = nChild)
    {
        // 子结点的位置是 父结点位置 * 2 + 1
        nChild = 2 * i + 1;
        // 得到子结点中较大的结点
        if (nChild != nLength - 1 && array[nChild + 1] > array[nChild])
            ++nChild;
        // 如果较大的子结点大于父结点那么把较大的子结点往上移动,替换它的父结点
        if (nTemp < array[nChild])
        {
            array[i] = array[nChild];
        }
        else    // 否则退出循环
        {
            break;
        }
    }
    // 最后把需要调整的元素值放到合适的位置
    array[i] = nTemp;
}
// 堆排序算法
void HeapSort(int array[], int length)
{
    // 调整序列的前半部分元素,调整完之后第一个元素是序列的最大的元素
    for (int i = length / 2 - 1; i >= 0; --i)
    {
        HeapAdjust(array, i, length);
    }
    // 从最后一个元素开始对序列进行调整,不断的缩小调整的范围直到第一个元素
    for (int i = length - 1; i > 0; --i)
    {
        // 把第一个元素和当前的最后一个元素交换,
        // 保证当前的最后一个位置的元素都是在现在的这个序列之中最大的
        Swap(&array[0], &array[i]);
        // 不断缩小调整heap的范围,每一次调整完毕保证第一个元素是当前序列的最大值
        HeapAdjust(array, 0, i);
    }
}



/*堆排序 实现,算法复杂度O(nlgn)*/
#include<stdio.h>
#include<stdlib.h>

/*假设节点i的左右子树都是最大堆,操作使节点i的子树变成最大堆*/
void maxHeap(int A[],int len,int i)
{
    int l,r,large,temp;
    l=2*i;
    r=2*i+1;
    large=i;
    if(l<len)
    {
        if(A[l]>A[i])
        {
            large=l;
        }
    }
    if(r<len)
    {
        if(A[r]>A[large])
        {
            large=r;
        }    
    }

    if(large!=i)
    {
        temp=A[large];
        A[large]=A[i];
        A[i]=temp;
        maxHeap(A,len,large);
    }
}

/*建立大根堆*/
void buildMaxHeap(int A[],int len)
{
    int i;
    for(i=len/2-1;i>=0;i--)
        maxHeap(A,len,i);
}


/*堆排序*/
void maxHeapSort(int A[],int len)
{
    int i,temp;
    buildMaxHeap(A,len);
    printf("建立大跟堆\n");
    for(i=0;i<len;i++)
        printf("%d ",A[i]);
    printf("\n");

    for(i=len;i>1;i--)
    {
        temp=A[0];
        A[0]=A[i-1];
        A[i-1]=temp;
        printf("%d  ",A[i-1]);
        buildMaxHeap(A,i-1);
    }
    printf("\n");
}

/*测试堆排序*/
int main()
{
    int i;
    int A[11]={4,1,3,2,16,9,10,14,8,7,6};
    maxHeapSort(A,11);
    
    for(i=0;i<11;i++)
    {
        printf("%d  ",A[i]);
    }
    printf("\n");
}



4.算法时间复杂度
  堆排序中 heap 算法的时间复杂度与堆所对应的完全二叉树的树高度 log2n 相关。而 heapsort 中对 heap 的调用数量级为n,所以堆排序的整个时间复杂度为O(nlog2n) 。并且堆排序是不稳定的





评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值