最小堆排序

堆排序分为最大堆排序和最小堆排序,今天我们来说一说最小堆排序。最小堆是完全二叉树的一种,它的特征是每个父亲结点都小于其两个孩子结点,至于两个孩子结点的大小,不做要求。

最小堆排序的核心函数是筛选函数,也就是建立最小堆的函数

typedef int ElemType;

//最小堆排序

void sort_heap(ElemType *ar, int end)
{
    ElemType temp = 0;
    while (end >0)
    {
        Swap(ar[end], ar[0]);
        FilerDown(ar,0, end-1);
        end -= 1;
    }

}

void make_heap(ElemType *ar, int n)

{
    int end = n - 1;
    int pos = (end - 1) / 2;
    while (pos >= 0)
    {
        FilerDown(ar, pos,end);
        --pos;
    }

}

void FilerDown(ElemType *ar, int start, int end)
{
    int i = start;
    int j = 2 * i + 1;
    ElemType tmp = ar[i];
    while (j<=end)
    {
        if (j < end && ar[j] > ar[j+1])
            j += 1;
        if (tmp <= ar[j])
            break;
        ar[i] = ar[j];
        i = j;
        j = 2 * i + 1;
    }
    ar[i] = tmp;

}



void Swap(ElemType &a, ElemType &b)
{
    assert(a != NULL && b != NULL);
    ElemType temp = a;
    a = b;
    b = temp;
}

这里的FilerDown函数就是所谓的筛选函数,有三个参数,要排序的数组ar,注意这里的参数start指的不一定是所构造的完全二叉树的第一个结点,而是其中任意一个非叶子结点(有孩子结点的结点)的下标,而end就是完全二叉树的最后一个叶子结点的下标在这个函数中我们用一个变量i表示要构造最小堆的结点的下标(start),j变量表示它的左孩子(2*i+1是该结点的左孩子,2*i+2是该结点的右孩子);将i下标所在的 结点用tmp变量保存起来,当j小于等于最后一个结点的下标即i的左孩子存在时一直循环,如果j不是最后一个结点下标并且j+1结点值小于j(右孩子大于左孩子),就把j+=1;这样做的原因是,每次i结点都与它两个孩子中值较小的一个比较,如i比它两个孩子最小的都小,直接跳出循环,因为此时已经满足最小堆的条件,否则把它的较小的 孩子结点赋给它(i已经在tmp中保存),i移动到它的叶子结点的 位置,j再更新到此时i结点的左孩子,然后重复上面的过程,这样当j大于最后一个节点时,该结点所在的分支已经满足最小堆的条件

注意这个Flier Down函数是在make_heap函数中嵌套的,这个make_heap函数执行完之后整个数组就已经成为最小堆,再调用sort_heap函数,也就是最后一步,排序,这里的排序思想是:因为再执行make_heap函数时整个数组已经是一个最小堆,所以根据最小堆的定义该数组的首元素一定是值最小的一个,调用swap函数把首元素也就是第一个结点与最后一个结点互换,此时数组中最小的元素是完全二叉树的最后一个结点,其他结点依次迁移,这时,之前已经构造成功的最小堆被破坏,需要重新执行Filer Down函数,但由于最小元素已经找到,所以其所在结点不用参加构造过程,即将它的前一个结点置为最后一个叶子结点,直到执行到最后一个叶子结点是第一个节点时,我们的最小堆排序就完成了

注意这里的完全二叉树 是逻辑上的 ,在实际上就是对数组进行的操作

阅读更多
想对作者说点什么?

博主推荐

换一批

没有更多推荐了,返回首页