lionel chang

导航

内部排序总结(三)选择类排序

一.简单选择排序

简单选择排序在每趟中选择一个最小的数,与当前关键字进行比较。如果不相同,交换二数。如此进行len-1趟。

void selectSort(int arr[],int len)
{
///升序
        int i,j,min;
        for(i=1;i<len-1;i++)
        {   
                min=i;
                for(j=i+1;j<len;j++)
                {   
                        if(arr[j]<arr[min])
                                min=j;
                }   
                if(min!=i)
                {   
                        arr[0]=arr[i];
                        arr[i]=arr[min];
                        arr[min]=arr[0];
                }   
        }   
}

二.堆排序

1.堆的定义

每个非叶子节点的值或者不大于左右孩子节点的值(小顶堆,图2),或者不小于左右孩子节点的值(大顶堆,图1)。

堆排序(以大顶堆为例,小顶堆类似)的基本思路是:将最大元素找出来,放到最后一个位置,然后将次大元素放到倒数第二个位置,以此类推。找到最大元素是通过完全二叉树实现的,即让每个节点都不小于左右孩子节点。

在堆排序中要解决两个问题:i.如何将一个无序序列建成一个堆。ii。如何将堆顶元素输出后,让剩余元素成为另一个堆。其中第一个问题依赖于第二个问题,所以我们先讨论第二个问题。

2.调整剩余元素,重新成为一个新堆

以无序序列{49,38,65,97,76,13,27,(49)}为例,如下图进行调整:首先,如图1,将最大元素(即根97)放到最后一个位置去;然后,因为根结点38并不比孩子节点65和49大,所以要交换;如图3,父节点38大于孩子节点13和27,所以不用在交换;调整完毕。

代码如下:

void heapAdjust(int arr[],int start,int end)//调整为一个大根堆
{
        int i,j;
        arr[0]=arr[start];//arr[0]保存要调整的值
        for(j=2*start;j<=end;j*=2){//根据完全二叉树的性质,左孩子节点下标=2*父节点下标;所以每次乘2,得到孩子节点
                if(j<end&&arr[j]<arr[j+1])      j++;//若右孩子大于左孩子的值,j指向右孩子
                if(arr[0]>=arr[j])              break;//若被调整的值大于左右孩子的值,则停止调整
                arr[start]=arr[j];//将左右孩子中较大的值,上移到父节点
                start=j;//start指向孩子节点,准备下一轮调整
        }   
        arr[start]=arr[0];//将被调整值放到了合适的位置
}



3.创建新堆

下图是由{49,38,65,97,76,13,27,(49)}创建新堆的过程,可以看出,基本思想是:从最后一个非叶子节点(图中的97)开始,到根结点每个都用上面的方法进行调整。图2和图3中需要调整的节点的值均大于左右孩子,所以不用调整;图4中,先将38与97交换,发现38仍小于(49),继续交换。得到图5;图5中由于根节点49小于97,所以交换二者;最终得到图6所示的堆;

代码如下:

  for(i=len/2;i>0;i--)//从最后一个非终端节点到根结点从后向前调整,建立一个大根堆
                heapAdjust(arr,i,len);



完整代码如下:

#include<stdio.h>
#define NO_NUM 0
void heapAdjust(int arr[],int start,int end);
void heapSort(int arr[],int len)
{
///升序
        int i,j,temp;
        for(i=len/2;i>0;i--)//从最后一个非终端节点到根结点从后向前调整,建立一个大根堆
                heapAdjust(arr,i,len);
        for(i=len;i>=2;i--)
        {   
                temp=arr[1];//将最大值交换到最后一个位置处
                arr[1]=arr[i];
                arr[i]=temp;
                heapAdjust(arr,1,i-1);//重复交换
        }   
}
void heapAdjust(int arr[],int start,int end)//调整为一个大根堆
{
        int i,j;
        arr[0]=arr[start];//arr[0]保存要调整的值
        for(j=2*start;j<=end;j*=2){//根据完全二叉树的性质,左孩子节点下标=2*父节点下标;所以每次乘2,得到孩子节点
                if(j<end&&arr[j]<arr[j+1])      j++;//若右孩子大于左孩子的值,j指向右孩子
                if(arr[0]>=arr[j])              break;//若被调整的值大于左右孩子的值,则停止调整
                arr[start]=arr[j];//将左右孩子中较大的值,上移到父节点
                start=j;//start指向孩子节点,准备下一轮调整
        }   
        arr[start]=arr[0];//将被调整值放到了合适的位置
}
int main(void)
{
        int i;
        int arr[8]={NO_NUM,49,38,65,97,76,13,27};
        heapSort(arr,8);
        for(i=1;i<8;i++)
        {   
                printf("%d  ",arr[i]);
        }   
        printf("\n");
}


PS: 在linux找一个好点的画图工具不容易啊。先用LibreOffice draw 画图,文件只能被保存为一种没见过的格式(CSDN 上传只支持jpg .gif .png .bmp),然后下了一个gimp和dia,同样是各种问题。又有人说gpaint是windows的画图工具的替代品,结果下载下来,文件格式倒是什么都支持,但是连个撤销功能都没有,那叫一个晕啊。抓狂现在下了一个inkscape用着还不错(问题是:不能支持jpg,gif,bmp,只支持png和一些软件自己才能识别的格式;而且文件被保存为png后,不能二次编辑了)

4.时间复杂度

堆排序也是一种先进的排序算法(时间复杂度为O(nlogn的算法)),算法复杂度为O(nlog2^n)

5.空间复杂度

因为堆排序只需要一个记录的空间用于存放要调整节点的值,所以空间复杂度为O(1)

6.稳定性

堆排序是一种不稳定算法

7.适用对象

有上面的讨论知道,堆排序的主要运行时间用于创建堆和调整堆。因此,堆排序更适合于记录数多的场合。





参考:

《数据结构》 严蔚敏

http://edsionte.com/techblog/archives/3817

http://www.cnblogs.com/zabery/archive/2011/07/26/2117103.html


posted on 2012-08-26 18:39  woshizyl  阅读(137)  评论(0编辑  收藏  举报