内部排序总结(三)选择类排序
一.简单选择排序
简单选择排序在每趟中选择一个最小的数,与当前关键字进行比较。如果不相同,交换二数。如此进行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