• 《编程珠玑》阅读小记(11) — 堆


    章节简述

    本章主要介绍堆,用该数据结构解决下面两个重要的问题:

    • 排序,采用堆排序算法对n元数组排序,所花的时间不会超过O(nlogn),而且只需要几个字的额外空间;
    • 优先级队列,堆通过插入新元素和提取最小元素这两种操作来维护元素集合,每个操作所需的时间都为O(logn);

    本章采用自底向上的组织结构,从细节开始逐步过渡到正题。

    堆数据结构

    该部分介绍堆数据结构的设计思想。

    优先级队列实现向量排序算法

    优先级队列提供了一种简单的向量排序算法,优先在优先级队列中依次插入每个元素,然后按序删除它们,程序实现代码如下:

    /************************************************************************/
    /* 
     * 优先级队列的类实现
     */
    /************************************************************************/
    
    #ifndef _PRIQUEUE_H_
    #define _PRIQUEUE_H_
    
    #include <iostream>
    
    template<typename T>
    class PriQueue{
    private:
        int n, maxsize;
        T *x;
    
        void swap(int i, int j)
        {
            T temp = x[i];
            x[i] = x[j];
            x[j] = temp;
        }
    
    public:
        PriQueue(int m) :maxsize(m)
        {
            x = new T[maxsize + 1];
            n = 0;
        }
    
        void insert(T t)
        {
            int i, p;
            x[++n] = t;
    
            //自底向上类似siftup函数内容实现优先级序列
            for (i = n ; i > 1 && x[p=i/2] > x[i] ;  i = p)
                swap(p, i);
        }
    
        //输出队列顶并调整队列结构
        T extramin()
        {
    
            /*cout << "队列中的数据为:" << endl;
            for (int i = 1; i <= n; i++)
            cout << x[i] << "	";
            cout << endl;*/
    
            int i, c;
            T t = x[1];
            x[1] = x[n--];
    
            //自顶向下调整队列结构
            for (i = 1; (c = 2 * i) <= n; i = c)
            {
                if (c + 1 <= n && x[c + 1] < x[c])
                    c++;
    
                if (x[i] <= x[c])
                    break;
    
                swap(c, i);
            }
    
    
            return t;
        }
    };
    #endif

    main主程序实现如下:

    /************************************************************************/
    /* 《编程珠玑》第十四章 堆
    * 问题:程序的输入包含两个整数m和n,其中m<n。输出是0~n-1范围内m个随机整数的有序列表,不允许重复
    * 方案:使用堆数据结构思想,构造优先级队列,设计出一种向量排序算法
    */
    /************************************************************************/
    
    #include <iostream>
    #include <algorithm>
    #include <cstdlib>
    #include "PriQueue.h"
    using namespace std;
    
    
    /************************************************************************/
    /* 解决问题的向量排序算法                                                 */
    /************************************************************************/
    template<typename T>
    void pqsort(T *v , int n)
    {
        PriQueue<T> pq(n);
        for (int i = 0; i < n; i++)
        {
            pq.insert(v[i]);
        }
    
        for (int j = 0; j < n; j++)
        {
            v[j] = pq.extramin();
        }
    }
    
    const int N = 10;
    int main()
    {
        int arr[N] = { 4, 12 , 56 , 32 , 24 , 68 , 33 , 6 , 7 , 2 };
        cout << "排序前输入数据为 :"<<endl;
        for (int j = 0; j < N; j++)
        {
            cout << arr[j] << "	";
        }
        cout << endl;
    
        pqsort(arr,N);
    
        cout << "排序后输出数据为 : " << endl;
        for (int j = 0; j < N; j++)
        {
            cout << arr[j] << "	";
        }
        cout << endl;
    
        system("pause");
        return 0;
    }

    对于上面实现的优先级队列向量排序算法,n次insert和extractmin操作在最坏情况下的开销是O(nlogn),优于快速排序算法的最坏O(n^2)的复杂度,但是缺点是,该算法需要额外的n+1的字节的空间来存储数组x[0…n];

    下面讨论的堆排序,改进了基于优先级队列的向量排序算法,代码更加简洁,而且不需要辅助数组,使用的空间更少。

    堆排序算法

    对于优先级队列实现的向量排序算法,需要两个数组,一个用于优先级队列,一个用于待排序的元素;而堆排序算法只需要一个数组,因此节省了空间开销。
    思想:使用单个数组x同时表示两种抽象结构,左边是堆,右边是输入元素序列。元素的初始顺序是随意的,而最终则是有序的。
    算法实现:

    这里写代码片

    原理

    本章最终总结以下几个原理:

    • 高效性,堆数据结构形状保证了堆中所有节点和根节点之间相差的层数在logn内,由于树是平衡的,所以函数siftup和siftdown的运行效率很高。堆排序通过在同一个实现数组中包含两种抽象结构(堆和元素序列)来避免使用额外的开销;
    • 正确性;
    • 抽象性,一个好的工程师能够分清某个组件做什么(用户看到的抽象功能)和如何做(黑盒实现)之间的差别;
    • 过程抽象;
    • 抽象数据类型:数据类型做什么是由它的方法和方法的规范给出的,而如何做则是由具体实现决定的;
  • 相关阅读:
    Quartus 自定义一个IP核,通过AXI总线被HPS系统控制(未完待续)
    IR 发送数据的上升沿和下降沿的判断(边沿检测)
    机器学习特征工程和优化方法
    最大期望算法(EM)
    主题模型(Topic Model)
    马尔科夫(Markov)
    【mysql-02-2】使用文档-建表约束
    【mysql-02-3】使用文档-范式
    【mysql-02-1】使用文档-基本语法
    Linux身份鉴别机制原理
  • 原文地址:https://www.cnblogs.com/shine-yr/p/5214942.html
Copyright © 2020-2023  润新知