堆排序算法

2 篇文章 0 订阅
2 篇文章 0 订阅

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

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

二、堆

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

这里写图片描述

如上图可以就是一个堆,数据结构可以视为一棵完全二叉树,数组表示如右图,假设该数组名为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<stdio.h>
#include<stdlib.h>

#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(l<len && arr[l]>arr[max])
        max=l;      
    if(r<len && arr[r]>arr[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;i<len;++i)
        printf("%d  ",arr[i]);
    printf("\n");

    heap_sort(arr,len);

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

非递归版本

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


#include<stdio.h>
#include<stdlib.h>

#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)<len) //左子树在边界范围内,说明非叶子结点  继续
    {
        l=LEFT(start);
        r=RIGHT(start);
        max = start;

        if(l<len && arr[l]>arr[max])
            max = l;
        if(r<len && arr[r]>arr[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;
}

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值