• 堆排序,C++模板编程


    明天就要去参加百度的笔试了,现在来抱抱佛教。

    理论来自:简明现代魔法

    在程序设计相关领域,堆(Heap)的概念主要涉及到两个方面:

    • 一种数据结构,逻辑上是一颗完全二叉树,存储上是一个数组对象(二叉堆)。
    • 垃圾收集存储区,是软件系统可以编程的内存区域。

    本文所说的堆,指的是前者。

    堆排序的时间复杂度是O(nlgN),与快速排序达到相同的时间复杂度。但是在实际应用中,我们往往采用快速排序而不是堆排序。这是因为快速排序的一个好的实现,往往比堆排序具有更好的表现。堆排序的主要用途,是在形成和处理优先级队列方面。另外,如果计算要求是类优先级队列(比如,只要返回最大或者最小元素,只有有限的插入要求等),堆同样是很适合的数据结构。

    基础知识

    堆一般用数组表示,比如数组A数组的长度Length(A),堆在数组中的元素个数HeapSize(A)。一般说来,HeapSize(A) <= Length(A),因为数组A当中可能有一些元素不在堆中。

    假设节点I是数组A中下标为i的节点。

    • Parent(i) : return Floor(i/2); //I的父节点下标,Floor(i)表示比i小的最大整数。
    • Left(i) : return 2*i; //I的左子节点
    • Right(i) : return 2*i+1; //I的右子节点

    含有n个元素的堆A的高度是: Floor(lgn)。

    堆的基本操作
    • MaxHeapify( A, i ):

      保持堆的性质。假设数组A和下标i,假定以Left(i)和Right(i)为根结点的左右两棵子树都已经是最大堆,节点i的值可能小于其子节点。调整节点i的位置。

    • BuildMaxHeap( A ):

      从一个给定的数组建立最大堆。子数组A[ floor(n/2)+1 .... ... n]中的元素都是树的叶节点(完全二叉树的基本性质)。从索引 ceiling(n/2)开始一直到1,对每一个元素都执行MaxHeapify,最终得到一个最大堆。

    • 堆排序 HeapSort( A ):

      堆排序算法的基本思想是,将数组A创建为一个最大堆,然后交换堆的根(最大元素)和最后一个叶节点x,将x从堆中去掉形成新的堆A1,然后重复以上动作,直到堆中只有一个节点。

    • 优先级队列算法-增加某元素的值(优先级) : HeapIncreaseKey( A, i, key )

      增加某一个元素的优先级后(元素的值),该元素应该向上移动,才能保持堆的性质。

    • 优先级队列算法-插入一个元素: Insert( S, x ) 将x元素插入到优先级队列S中。

      主要思路是,将堆的最后一个叶节点之后,扩展一个为无穷小的新叶节点,然后增大它的值为x的值。

    本来我只打算随便写个小程序的,但是发现,可以写个更通用的程序。以最小堆为例,说下建堆和排序的过程。

    heapSort_1

    heapSort_2

    经过上面两个步骤,就成功的建立了一个最小堆。排序的过程就是取出堆顶元素push到临时数组,然后将堆顶元素和最后一个元素交换,再pop掉最后一个元素,直到堆中没有元素。这样就获得了一个有序的数组,然后在复制到堆中。

    完整程序如下:

    //最小堆排序和最大堆排序
    
    #include <algorithm>
    #include <functional>
    #include <vector>
    #include <iostream>
    using namespace std;
    
    template<typename Type>
    class Heap
    {
    public:
        Heap(const vector<Type>& a_array)
        {
            m_array.assign(a_array.begin(),a_array.end());
        }
    
        template<typename Compare>
        void sort(Compare comp);
    
        void printArray(const vector<Type>& a_array);
    
    private:
        vector<Type> m_array;
    
        //comp 为less<Type> 则大数下沉,创建最小堆,从小到大排序
        //comp 为greater<Type> 则小数下沉,创建最大堆,从大到小排序
        template<typename Compare>
        void creatHeap(Compare comp);                //创建堆
    
        template<typename Compare>
        void downElement(int a_elem, Compare comp);    //下沉元素
    };
    
    template<typename Type>
    template<typename Compare>
    void Heap<Type>::sort(Compare comp)
    {
        printArray(m_array);
        creatHeap(comp);                    //建堆
        vector<Type> array;
        for (int i = m_array.size() - 1; i >= 0; i--)
        {
            array.push_back(m_array[0]);    //保留堆顶
            swap(m_array[0], m_array[i]);    //交换
            m_array.pop_back();                //去掉最后一个元素
            downElement(0,comp);            //将新的首元素下沉
        }
        printArray(array);
        m_array.assign(array.begin(),array.end());
    }
    
    template<typename Type>
    template<typename Compare>
    void Heap<Type>::creatHeap(Compare comp)
    {
        //从最后一个非叶子节点开始,将每个父节点都调整为最小堆
        for (int i=m_array.size()/2-1; i>=0; i--)
        {
            downElement(i, comp);
        }
    }
    
    template<typename Type>
    template<typename Compare>
    void Heap<Type>::downElement(int a_elem, Compare comp)    //下沉元素
    {
        int min;            //设置最小元素下标
        int index = a_elem;    //当前下沉的元素下标
        while (index*2+1 < m_array.size())//存在左节点
        {
            min = index*2+1;
            if (index*2+2 < m_array.size())//存在右节点
            {
                //左右节点比较,选出最小的
                if (comp(m_array[index*2+2],m_array[min]))
                {
                    min = index*2+2;
                }
            }
            //同子节点比较,若父节点最小则结束
            if (comp(m_array[index],m_array[min]))
            {
                break;
            }
            else//选最小元素到父节点
            {
                swap(m_array[min],m_array[index]);
                index = min;
            }
        }
    }
    
    template<typename Type>
    void Heap<Type>::printArray(const vector<Type>& a_array)
    {
        for (int i=0; i<a_array.size(); i++)
        {
            cout << a_array[i] << " ";
        }
        cout << endl;
    }
    
    
    int main()
    {
        vector<int> array;
        for (int i=10; i<20; i++)
        {
            array.push_back(i);
        }
        random_shuffle(array.begin(), array.end());//打乱顺序
        Heap<int> heap(array);
        heap.sort(less<int>());
        heap.sort(greater<int>());
        return 0;
    }

    image

    作者:涵曦www.hanxi.cc
    出处:hanxi.cnblogs.com
    GitHub:github.com/hanxi
    Email:im.hanxi@gmail.com
    文章版权归本人所有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。

    《 Skynet 游戏服务器开发实战》

  • 相关阅读:
    状态压缩 + 暴力 HDOJ 4770 Lights Against Dudely
    简单几何(推公式) UVA 11646 Athletics Track
    简单几何(四边形形状) UVA 11800 Determine the Shape
    简单几何(求交点) UVA 11437 Triangle Fun
    计算几何模板
    简单几何(相对运动距离最值) UVA 11796 Dog Distance
    简单几何(求划分区域) LA 3263 That Nice Euler Circuit
    覆盖的面积 HDU
    Desert King 最小比率生成树 (好题)
    约会安排 (区间合并)毒瘤题
  • 原文地址:https://www.cnblogs.com/hanxi/p/2721540.html
Copyright © 2020-2023  润新知