码迷,mamicode.com
首页 > 其他好文 > 详细

【堆】

时间:2015-10-22 00:00:42      阅读:399      评论:0      收藏:0      [点我收藏+]

标签:

看上去好像很简单的样子··然后折磨了我好久····

主要是没仔细弄明白。

堆分为最小堆和最大堆,以二叉树的形式存在,最小堆即根节点为整个树的最小值,最大堆则是根节点为最大值。

建堆(以最大堆为例):

首先数据以数组形式存储(int a[]或vector<int> a),若二叉树的根节点从0开始计数,则节点 i 的左右子节点的下标分别为2 * i + 1和 2 * i + 2(忘了怎么算可以画一棵树,就知道了)。

void heap_down(int a[], int id, int numsSize)
{
    int leftchild = id * 2 + 1;
    int rightchild = leftchild + 1;
    if(leftchild <= numsSize-1)
    {
        if(rightchild <= numsSize - 1)
            if(a[leftchild] < a[rightchild])
                leftchild = rightchild;//根节点与左右子节点中最大的交换
        if(a[id] < a[leftchild])
        {
            swap(a[id], a[leftchild]);
            heap_down(a, leftchild, numsSize);//递归
        }
        
    }
}

到这里把向下建堆写好了,这个函数是每次能够建一个最大堆,我们堆排序还需要把堆顶元素与堆末尾元素交换,然后size--,再从堆顶向下建堆,循环size次完成堆排序,时间复杂度O(nlogn)。

void heap_sort(int a[], int numsSize)
{
    for(int i = numsSize/2;i>=0;i--)//为什么一定要从后往前heap_down
        heap_down(a,i,numsSize);
    int size = numsSize-1;//这里要保存一下数组的长度,因为必须要循环这么多次才能把所有的元素都排序一遍
    for(int j=0;j<=size;j++)
    {
        swap(a[0],a[numsSize-1]);
        numsSize--;
        heap_down(a,0,numsSize);
    }
}

接下来到了我之前一直犯错的地方:

为什么一定要从往前进行heap_down操作?

其实只要画个图就显而易见了,如果是从前往后循环的调用heap_down,首先第一次就会将堆顶元素和它的左右子节点中的较大者交换,然后················堆顶元素就再也变不了了,所有根本无法建立最大堆了!

为什么惩罚自己如此愚蠢,花时间写了这么多字的blog,所以一定要记住这里的坑。

另外由于heap_down操作自身会递归向下调用,所以在heap_sort中只要进行size/2次操作就可以完成最大堆的建立,虽然总体时间上并没有什么提升。

【堆】

标签:

原文地址:http://www.cnblogs.com/puyangsky/p/4899426.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!