算法导论复习(3) 堆排序

   堆排序与归并排序具有相同的时间复杂度O(nlgn),但是在讲堆排序之前,先要搞清楚堆排序使用的“二叉堆”

二叉堆是一个数组,可以被看成近似的完全二叉树
特点:
1.树上每一节点对应一个元素,除最底层外,树是完全充满的,而且从左到右填充。
2.
a(大顶堆):根节点的值是大于等于任何子节点的值
b(小顶堆):根节点的值是小于等于任何子节点的值
3.每个节点的左子树和右子树均为一个二叉堆
通过以上特点,将一个数组的所有元素依次放入二叉堆中,设数的根节点为a[i],给定一个节点下标为i,则可以得到节点的父节点,左孩子和右孩子的下标:
父节点:i/2
左孩子:2*i
右孩子:2*i+1
介绍完基本知识,书本上介绍了最大堆排序的步骤及时间复杂度:

MAX-HEAPIFY(对于单个二叉堆的排序算法) 时间复杂度O(lgn)
BUILD-MAX-HEAP(建二叉堆算法) 时间复杂度O(n)
HEAPSORT(对数组排序) 时间复杂度O(nlgn)

一、MAX-HEAPIFY
这个步骤的目的是维护最大堆性质,输入为数组a和下标i,在调用MAX-HEAPIFY时,假定左子树和右子树均为最大堆,但a[i]可能小于孩子,所以通过让a[i]逐级下降,使得下标i为根节点的子树满足最大堆性质。

代码展示:

int maxheapify(int *a,int i,int size)
{
    int l=2*i;           //左子树
    int r=2*i+1;         //右子树
    int largest;         
    if(i<=size/2)
{
if(l<=size&&a[l]>a[i])           //左孩子与根节点
    {
        largest=l;
    }
    else  
    {
        largest=i;
    }
if(r<=size&&a[r]>a[largest])     //右孩子与根节点
    {
        largest=r;
    }
    if(largest!=i)             
    {
        swap(a[i],a[largest]);
        maxheapify(a,largest,size);
    }
}
}

分析时间代价:
1。调整a[i]和左孩子、右孩子关系θ(1)
2。以i的一个孩子为根节点的子树运行MAX-HEAPIFY时间
递归式:T(n)=T(2n/3)+θ(1)
递归式的解为O(lgn)(主定理方法2),所以树高为n的结点,MAX-HEAPIFY时间复杂度为O(n)

二、建堆
根节点下标为a.length/2,叶节点下标为(n/2+1)、(n/2+2)…(n),所以对a.length/2到1做MAX-HEAPIFY操作
int buildheap(int *a,int size)
{
for(int i=(size/2);i>=1;i–)
{
maxheapify(a,i,size);
}
}
建堆的时间复杂度在88页有详细的介绍,这里不详细介绍了,主要的就是建堆的时间复杂度是O(n).

三、堆排序
对大顶堆,因为根节点的值是数组中也是二叉堆中最大的,所以每次把根节点取走,然后剩下的排出最大根节点再取走,依次就可以完成数组从大到小排序,反之从小到大排序是在小顶堆中每次取根节点所得。
代码:

int heapsort(int *a,int size)
{
    buildheap(a,size);
    for(int i=size;i>=1;i--)
    {
        swap(a[1],a[i]);
        size=size-1;
        maxheapify(a,1,size);
    }
}

因为是建堆的时间复杂度是O(n),要调用MAX-HEAPIFY(n-1)次,每次时间O(lgn),所以堆排序的时间复杂度是O(nlgn)

点赞