堆排序算法

一、堆排序算法的基本特性

时间复杂度:O(nlgn) 与归并排序相同
最坏:O(nlgn)
空间复杂度:O(1).
不稳定。

二、堆

堆的实现通过构造二叉堆(binary heap),实为二叉树的一种;由于其应用的普遍性,当不加限定时,均指该数据结构的这种实现。这种数据结构具有以下性质。
* 任意节点小于(或大于)它的所有后裔,最小元(或最大元)在堆的根上(堆序性)。
* 堆总是一棵完全树。即除了最底层,其他层的节点都被元素填满,且最底层尽可能地从左到右填入。
* 将根节点最大的堆叫做最大堆或大根堆,根节点最小的堆叫做最小堆或小根堆。

堆排序算法_第1张图片

如上图可以就是一个堆,数据结构可以视为一棵完全二叉树,数组表示如右图,假设该数组名为A。
如上图所示,树的根为A[1],若父节点为A[i],则其左子树下标为A[i/2],右子树下标为A[i/2+1]。
伪代码如此表示

PARENT(i)
  return [i/2]

LEFT(i)
  return 2i

RIGHT(i)
  return 2i+1

* 注意:数组下标第一个是0,不是1 *
在堆排序算法中,使用的是最大堆,在最大堆中,最大堆性质是指除了根以外的所有接点i都满足A[PARENT(i)]>=A[i].
最小堆通常用于构造优先队列。

三、维护堆的性质

下面伪代码用于维护最大堆性质,称为MAX-HEAPIFY,输入一个数组A和一个下标i,调用MAX-HEAPIFY,假定其左右子树的二叉树都是最大堆,这个时候A[i]有可能小于其孩子,违背最大堆性质。此代码将A[i]的值在最大堆中逐级下降,使重新遵循最大堆性质。

MAX-HEAPIFY
堆排序算法_第2张图片

简而言之,上面代码就是将当前结点与比他大的最大的子结点交换,并如此递归,当然,如果两个子结点都比他小,就不用交换了。
执行的过程如下图,堆i=2执行

堆排序算法_第3张图片

既然知道如何维护堆的性质后,那么接下来就是把要排序的东东建堆了

四、建堆

思想 : 自底向上维护最大堆性质,那么我们就可以获得一个从根节点开始的最大堆
伪代码 :
堆排序算法_第4张图片

看一个建堆的具体过程
堆排序算法_第5张图片

五、堆排序

经过上面的一些步骤,我们已经把最大的数字放到了根节点,那接下来我们只需要一次取出根结点,然后维护堆的性质(这时候剩根节点又是剩下的最大值),就可以获得一个排序好的数组啦。
这里,我们每次使用没排序的最后的节点替换根节点,伪代码如下
堆排序算法_第6张图片

具体的运行过程
堆排序算法_第7张图片
堆排序算法_第8张图片

六、代码

递归版本,其实挺好理解的吧

/*************************************************************************
    > File Name: heap_sort.cpp
    > Author: chunquanL
    > Created Time: 2017-04-19
 ************************************************************************/

#include
#include

#define PARENT(i) (((i)-1)/2)
#define LEFT(i)   ((i)*2+1)  
#define RIGHT(i)  ((i)*2+2)  


void swap(int *a,int *b)
{
    *a^=*b;
    *b^=*a;
    *a^=*b;
}

//维护最大堆性质函数
void max_heapify(int arr[],int len,int i)
{
    int l = LEFT(i);
    int r = RIGHT(i);
    int max = i;
    if(larr[max])
        max=l;      
    if(rarr[max])
        max=r;

    if(max!=i)
    {
        swap(&arr[i],&arr[max]);
        max_heapify(arr,len,max);
    }
}


//堆排序函数
void heap_sort(int arr[],int len)
{
    //1.建堆
    for(int i=PARENT(len-1);i>=0;--i)
    {
        max_heapify(arr,len,i);
    }
    //2.排序
    for(int i=len-1;i>0;--i)
    {
        swap(&arr[0],&arr[i]);
        max_heapify(arr,i,0);
    }
}

int main()
{
    int arr[]={3, 5, 3, 0, 8, 6, 1, 5, 8, 6, 2, 4, 9, 4, 7, 0, 1, 8, 9, 7, 3, 1, 2, 5, 9, 7, 4, 0, 2, 6 };

    int len=sizeof(arr)/sizeof(arr[0]);
    for(int i=0;iprintf("%d  ",arr[i]);
    printf("\n");

    heap_sort(arr,len);

    for(int i=0;iprintf("%d  ",arr[i]);
    printf("\n");
}

非递归版本

/*************************************************************************
    > File Name: heap_sort2.cpp
    > Author: chunquanL
    > Created Time: 2017-04-19
 ************************************************************************/


#include
#include

#define PARENT(i) (((i)-1)/2)
#define LEFT(i)   ((i)*2+1)  
#define RIGHT(i)  ((i)*2+2)

void swap(int *a,int *b)
{
    *a^=*b;
    *b^=*a;
    *a^=*b;
}

void max_heapify(int arr[],int start,int len)
{
    int l,r,max;
    while(LEFT(start)//左子树在边界范围内,说明非叶子结点  继续
    {
        l=LEFT(start);
        r=RIGHT(start);
        max = start;

        if(larr[max])
            max = l;
        if(rarr[max])
            max = r;

        if(max!=start) //若此条件成立 则max为左右子树中的一个
        {
            swap(&arr[max],&arr[start]);
            start = max;
        }
        else //不成立,已满足特性,退出
            break;
    }
}


void heap_sort(int arr[],int len)
{
    //1.建堆
    for(int i=PARENT(len-1);i>=0;--i)
    {
        max_heapify(arr,i,len);
    }

    //2.排序
    while(--len)
    {
        swap(&arr[0],&arr[len]);
        max_heapify(arr,0,len);
    }
}


int main() {
    int arr[] = { 3, 5, 3, 0, 8, 6, 1, 5, 8, 6, 2, 4, 9, 4, 7, 0, 1, 8, 9, 7, 3, 1, 2, 5, 9, 7, 4, 0, 2, 6 };
    int len = (int) sizeof(arr) / sizeof(*arr);
    heap_sort(arr, len);
    int i;
    for (i = 0; i < len; i++)
        printf("%d ", arr[i]);
    printf("\n");
    return 0;
}

你可能感兴趣的:(算法,c/c++,数据结构)