zoukankan      html  css  js  c++  java
  • 数据结构基础(19) --堆与堆排序

    完全二叉树

     

    首先让我们回顾一下完全二叉树的两个性质:

      性质1:具有n个结点的完全二叉树的深度为[logn](向下取整)+1。

      性质2:若对含 个结点的完全二叉树从上到下且从左至右进行 至 的编号,则对完全二叉树中任意一个编号为 的结点:

        (1) 若 i=1,则该结点是二叉树的根,无双亲,否则,编号为 [i/2](向下取整)的结点为其双亲结点;
        (2) 若 2i>n,则该结点无左孩子,否则,编号为 2i 的结点为其左孩子结点;
        (3) 若 2i+1>n,则该结点无右孩子结点,否则,编号为2i+1 的结点为其右孩子结点。

     

    数组与完全二叉树

     

    从上图可以看出, 如果完全二叉树从上到下且从左至右进行从0至n-1进行编号,则对完全二叉树的性质需要修改如下:

       (1) 若 i=0,则该结点是二叉树的根,无双亲,否则,编号为 [(i-1)/2](向下取整)的结点为其双亲结点;
       (2) 若 2i+1>n,则该结点无左孩子,否则,编号为 2i+1 的结点为其左孩子结点;
       (3) 若 2i+2>n,则该结点无右孩子结点,否则,编号为2i+2 的结点为其右孩子结点。


    大顶堆与小顶堆

     

    堆是满足下列性质的数列{r1, r2, …,rn}:


    (小顶堆)


    (大顶堆)

     

    大顶堆的设计

    template <typename Type>
    class MaxHeap
    {
    public:
        MaxHeap(int _maxSize = 10);
        virtual ~MaxHeap();
    
        bool isEmpty() const;
        void push(const Type &item);
        void pop();
        const Type &top() const;
    
    private:
        //将堆的容量增大两倍
        void resize();
        //向上渗透
        void trickUp(int index);
        //向下渗透
        void trickDown(int index);
    
    private:
        Type *heapArray;
        int maxSize;
        //currentSize有两个含义:
        // 1.代表当前堆中已经存储的元素数
        // 2.代表当前完全二叉树的第一个空位
        int currentSize;
    };

    大顶堆的实现

    //构造
    template <typename Type>
    MaxHeap<Type>::MaxHeap(int _maxSize)
        : maxSize(_maxSize),currentSize(0)
    {
        if (maxSize < 1)
            throw std::length_error("heap size must >= 1");
    
        heapArray = new Type[maxSize];
    }
    //析构
    template <typename Type>
    MaxHeap<Type>::~MaxHeap()
    {
        delete []heapArray;
        heapArray = NULL;
        currentSize = 0;
    }
    //判空
    template <typename Type>
    bool MaxHeap<Type>::isEmpty() const
    {
        return currentSize == 0;
    }

    堆顶元素

    //查看堆顶元素
    template <typename Type>
    const Type &MaxHeap<Type>::top() const
    {
        if (isEmpty())
            throw std::underflow_error("heap is empty");
    
        return heapArray[0];
    }

    插入元素

    //插入
    template <typename Type>
    void MaxHeap<Type>::push(const Type &item)
    {
        //如果堆已满, 则需要对堆进行扩充
        if (currentSize == maxSize)
        {
            resize();
        }
        //将元素插入到堆的第一个空位置上
        heapArray[currentSize] = item;
    
        //维护堆的性质:向上渗透
        trickUp(currentSize);
        ++ currentSize;
    }
    //向上渗透, 将刚刚插入的元素移动到合适的位置上
    template <typename Type>
    void MaxHeap<Type>::trickUp(int index)
    {
        //根据完全二叉树的性质, 找到父节点
        int parent = (index-1)/2;
        Type bottom = heapArray[index];
    
        //循环终止条件
        // 1. index = 0 //bottom元素插入到根节点
        // 2. heapArray[parent] >= bottom   //bottom插入到parent节点的一个子节点上
        while ((index > 0) && (heapArray[parent] < bottom))
        {
            //父节点下移
            heapArray[index] = heapArray[parent];
            index = parent;
            parent = (parent-1)/2;
        }
        //插入
        heapArray[index] = bottom;
    }
    //将堆的大小加倍
    template <typename Type>
    void MaxHeap<Type>::resize()
    {
        int newSize = std::max(maxSize*2, 1);
        Type *newHeap = new Type[newSize];
        for (int i = 0; i < currentSize; ++i)
        {
            newHeap[i] = heapArray[i];
        }
    
        delete []heapArray;
        heapArray = newHeap;
        maxSize = newSize;
    }

    删除堆顶元素

    //删除
    template <typename Type>
    void MaxHeap<Type>::pop()
    {
        if (isEmpty())
            throw std::underflow_error("heap is empty");
    
        //只有一个元素
        if (currentSize == 1)
        {
            heapArray[0].~Type();
            currentSize = 0;
            return ;
        }
    
        //显示释放堆顶元素
        heapArray[0].~Type();
        //直接将最有一个元素放到堆顶,
        //并且currentSize-1
        heapArray[0] = heapArray[-- currentSize];
    
        //此时如果破坏了堆的性质:向下渗透
        trickDown(0);
    }
    //向下渗透
    template <typename Type>
    void MaxHeap<Type>::trickDown(int index)
    {
        int largeChild;
        Type top = heapArray[index];
    
        // 只需到达完全二叉树的最后一层
        // 的上一层即可
        // 循环的终止条件:
        // 1. index已经到达了最后一层(index >= currentSize/2 :找到了一个比较合适的位置)
        // 2. 在堆的中间找到了一个合适的位置(top >= heapArray[largeChild])
        while (index < currentSize/2)
        {
            int leftChild = (index*2)+1;
            int rightChild = leftChild + 1;
    
            //如果有右儿子节点, 而且右儿子节点还大于左儿子节点
            if (rightChild < currentSize && heapArray[rightChild] > heapArray[leftChild])
                largeChild = rightChild;
            else
                largeChild = leftChild;
    
            if (top >= heapArray[largeChild])
                break;
    
            //不然的话, 则需继续向下渗透
            heapArray[index] = heapArray[largeChild];
            // index需要向下搜索
            index = largeChild;
        }
        //插入
        heapArray[index] = top;
    }

    堆排序

      堆排序就是将元素一个一个插入到堆中, 然后再一个一个的取出来;

    //堆排序
    template <typename Type>
    void heapSort(Type *begin, Type *end)
    {
        int size = end-begin;
        MaxHeap<Type> maxHeap(size);
        //存入堆中
        for (Type *current = begin; current < end; ++ current)
            maxHeap.push(*current);
    
        //从堆中取出
        while (begin != end)
        {
            *begin = maxHeap.top();
            ++ begin;
            maxHeap.pop();
        }
    }
    
    template <typename Type>
    void heapSort(Type *array, int arraySize)
    {
        return heapSort(array, array+arraySize);
    }

    -测试代码:

    int main()
    {
        int array[20];
        for (int i = 0; i < 20; ++i)
        {
            array[i] = rand()%100;
            cout << array[i] << ' ';
        }
    
        heapSort(array, 20);
    
        cout << endl;
        for (int i = 0; i < 20; ++i)
        {
            cout << array[i] << ' ';
        }
        cout << endl;
    
        return 0;
    }

  • 相关阅读:
    E437: terminal capability "cm" required 错误出现的原因和解决方法
    IDEA 配置svn及使用
    IDEA 2018.3.3注册方法-2100年
    三大UML建模工具Visio、Rational Rose、PowerDesign的区别
    地图展示聚合效果Leaflet.markercluster
    Idea设置Maven报错:Unable to import maven project: See logs for details
    Netflix大部分的推荐机制都是采用机器学习的算法
    Netflix推荐系统:从评分预测到消费者法则
    Netflix推荐系统的最新解读:算法、商业价值与创新
    推荐系统算法总结
  • 原文地址:https://www.cnblogs.com/itrena/p/5926987.html
Copyright © 2011-2022 走看看