• 【堆】


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

    主要是没仔细弄明白。

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

    建堆(以最大堆为例):

    首先数据以数组形式存储(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次操作就可以完成最大堆的建立,虽然总体时间上并没有什么提升。

  • 相关阅读:
    python 调用dll 动态链接库 结构体参数及回调函数等示例
    逆向x64-small-trick
    Malloc碎碎念
    关于OF和CF
    月赛-Crackhash
    ARM64调试环境
    Apache Kafka(六)- High Throughput Producer
    Apache Kafka(五)- Safe Kafka Producer
    Apache Kafka(四)- 使用 Java 访问 Kafka
    Apache Kafka(三)- Kakfa CLI 使用
  • 原文地址:https://www.cnblogs.com/puyangsky/p/4899426.html
Copyright © 2020-2023  润新知