您的位置:首页 > 其它

堆排序Heap sort

2017-09-24 18:29 239 查看
经典排序算法 - 堆排序Heap sort

堆排序有点小复杂,分成三块

第一块,什么是堆,什么是最大堆

第二块,怎么将堆调整为最大堆,这部分是重点

第三块,堆排序介绍

第一块,什么是堆,什么是最大堆

什么是堆

这里的堆(二叉堆),指得不是堆栈的那个堆,而是一种数据结构。

堆可以视为一棵完全的二叉树,完全二叉树的一个“优秀”的性质是,除了最底层之外,每一层都是满的,这使得堆可以利用数组来表示,每一个结点对应数组中的一个元素.

数组与堆之间的关系





二叉堆一般分为两种:最大堆和最小堆。

什么是最大堆

堆中每个父节点的元素值都大于等于其孩子结点(如果存在),这样的堆就是一个最大堆

因此,最大堆中的最大元素值出现在根结点(堆顶)

节点与数组索引关系

对于给定的某个结点的下标i,可以很容易的计算出这个结点的父结点、孩子结点的下标,而且计算公式很漂亮很简约





第二块,怎么将堆调整为最大堆,这部分是重点

整个过程如下图所示

在4,14,7这个小堆里边,父节点4小于左孩子14,所以两者交换

在4,2,8这个小堆里边,父节点4小于右孩子8,所以两者交换





上图展示了一趟调整的过程,这个过程递归实现,直到调整为最大堆为止

第三块,堆排序介绍

堆排序就是把堆顶的最大数取出,

将剩余的堆继续调整为最大堆,具体过程在第二块有介绍,以递归实现

剩余部分调整为最大堆后,再次将堆顶的最大数取出,再将剩余部分调整为最大堆,这个过程持续到剩余数只有一个时结束

下边三张图详细描述了整个过程











 


堆排序C++实现

[cpp] view
plaincopyprint?

/* 

 * heapsort.cpp 

 * 

 *  Created on: 2016年3月30日 

 *      Author: Lv_Lang 

 */  

  

//堆排序  

#include <iostream>  

using namespace std;  

  

/*堆排序算法解析: 

核心思想在于建立最大堆(或最小堆), 

因为最大堆满足一个性质:所有的父节点都要比它的左右节点均要大, 

于是据此可以每次将最大的(根节点)抽取出来(实际上是放最后面), 

每次都抽取剩余部分的最大值,那最终得到的序列就是有序的。 

所以堆排序其实算是一种高级版的选择排序。 

建立最大堆的方法:首先从底层的父节点开始,把它的子树整成最大堆, 

然后往上整成最大堆,注意这里要从下往上。 

最后堆排序就是先建立一个最大堆,然后把根节点元素也就是最大的元素 

跟最后面的元素交换,这样一来,树就未必满足最大堆了,所以要重新 

进行堆的调整(不包括已经被抽取的最大元素)。这样下来,每次都抽取 

出子序列的最大元素,最终的序列就从小到大排好序了。*/  

  

void swap(int a[],int i,int j)  

{  

    int temp = a[i];  

    a[i] = a[j];  

    a[j] = temp;  

}  

  

void HeapAdjust(int *a,int i,int size)  

{  

    int left = 2*i;//左孩子节点  

    int right = 2*i + 1;//右孩子节点  

    int max = i;//暂存最大元素下标  

    if(left <= size && a[left] > a[max])  

        //这里不直接交换是因为如果交换则要进行递归,  

        //所以连同右边的也判断了再做可能的交换和做可能的递归,  

        //递归的原因则是因为你把人的老爸交换了,  

        //那人家的儿子可能就比继父要大了。  

        //swap(a,left,i);  

        max = left;  

    if(right <= size && a[right] > a[max])  

        max = right;  

    if(max != i)//表明当前的父节点并不是最大的  

    {  

        swap(a,i,max);  

        HeapAdjust(a,max,size);//有交换就要递归把子树也整成最大堆  

    }  

}  

  

void BuildHeap(int a[],int size)  

{  

    for(int i = size / 2;i >= 1;i--)//非叶节点最大下标为/2  

    {  

        HeapAdjust(a,i,size);  

    }  

}  

  

//堆排序对要排序的序列有个要求就是下标是从1开始到size的,而并非常用的0~size-1  

void HeapSort(int a[],int size)  

{  

    BuildHeap(a,size);//先建立初始的最大堆  

    for(int i = size;i >= 2;i--)  

    {  

        swap(a,1,i);//把最大的元素放到最后面  

        size--;//不再包括最大的元素  

        //这里之所以直接从根节点开始维护堆是因为除了根节点外的其他子树都是最大堆了。  

        //而上面第一次建立最大堆时不能保证父节点已经是最大的了,  

        //所以上面的需要先从底层的非叶节点层开始维护/调整堆。  

        HeapAdjust(a,1,size);  

    }  

    //至此数组就已经从小到大排好序了,  

    //如果要从大到小输出,则倒着输出就行了;  

    //而如果要将序列从大到小排序,则应建立最小堆。  

}  

  

void print(int a[],int size)  

{  

    for(int i=1;i < size;i++)  

        cout<<a[i]<<" ";  

    cout<<a[size]<<endl;  

}  

  

int main()  

{  

    int a[7] = {0,4,6,1,5,3,2};  

    cout<<"排序之前的序列为:"<<endl;  

    print(a,6);  

    HeapSort(a,6);  

    cout<<"排序之后的序列为:"<<endl;  

    print(a,6);  

  

    return 0;  

}  

关于堆排序算法,网上有更好更详细的解析,这里放一个我觉得挺好的解析博客,因为它有图:

参考文章

http://blog.kingsamchen.com/archives/547#viewSource
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: