堆排序

来源:互联网 发布:阿里云备案客服电话 编辑:程序博客网 时间:2024/06/16 09:09

堆的概念

堆的概念

小顶堆和大顶堆

通俗的说就是小顶堆本身的值小于或等于自己的任一个孩子节点(如果有孩子节点的话),这样看来,小顶堆中根节点的值是所有元素中最小的。大顶堆则刚好相反。

堆节点的访问

通常堆是通过一维数组来实现的。在数组起始位置为0的情形中:

  • 父节点i的左子节点在位置(2*i+1);
  • 父节点i的右子节点在位置(2*i+2);
  • 子节点i的父节点在位置floor((i-1)/2);

以下为上图中的大顶堆在一维数组中的的存储情况。

80 60 16 50 45 10 15 30 40 20

堆排序基本思想

根据堆的定义可以知道k[0]就是所有元素中最大的一个,把它与k[n-1]交换,变成下表的形式:

20 60 16 50 45 10 15 30 40 80

现在k[n-1]是最大的,它的位置就已经定下来了,再将K[0…n-2]调整为新的堆,如下:

60 50 16 40 45 10 15 30 20 80

接着把k[0]k[n-2]交换,再将K[0…n-3]调整为新的堆。
重复这样的操作,直到k[0]k[1]交换,完成排序。

现在就有两个问题,
第一个就是怎么样将一个初始序列建成堆的形式。
第二个就是删除根节点后,怎么将剩下的元素调整为新的堆。

初建堆

假设有数组如下:

1 3 4 5 7 2 6 8 0

这里写图片描述
这里写图片描述

将5和8,4和6交换位置之后,调整3的位置。
这里写图片描述

最后调整1的位置

这里写图片描述

这样就完成了建造大顶堆的过程。

删除根节点

这里写图片描述

以上为删除根节点的示意图,再将根节点删除之后,需要把堆的最后一个节点升为新的根节点,然后再对根节点进行位置调整,调整步骤同前面讲的步骤一样。
这也为什么进行排序时要将第一个元素和最后一个元素交换位置,这正好符合删除根节点的过程,这样就不必再重新创建一个数组来依次保存删除的元素。

c++实现

template<typename Type>void max_heapify(vector<Type> & a, int start, int end) {    int dad = start;    int son = dad * 2 + 1;    while (son < end)     {         if (son + 1 < end && a[son] < a[son + 1]) //先比较两个子节点的大小,选择较大的        {            son++;        }        if (a[dad] > a[son]) //如果父节点大于较大的子节点,则不需要调整        {            return;        }        else         { //否则交换位置,再继续子节点与孙节点的比较            swap(a[dad], a[son]);            dad = son;            son = dad * 2 + 1;        }    }}template<typename Type>void heap_sort(vector<Type> & a, int n) {    //初始化,i从最后一个非叶子节点开始    for (int i = n / 2 - 1; i >= 0; --i)    {//建造大顶堆        max_heapify(a, i, n);    }    for (int i = n - 1; i > 0; --i)     {//先将第一个元素和已经排好元素的前一位做交换(第一次交换时,和最后一个元素交换,交换完毕后,最后一位就是已排好的元素),     //再重新调整(刚调整的元素之前的元素),直到排序完毕。        swap(a[0], a[i]);        max_heapify(a, 0, i);    }}
0 0
原创粉丝点击