普林斯顿公开课 算法4-1:优先级队列API和基本实现

优先级队列是容器的一种,可以向优先级队列中添加或取出数据,取出数据时只能取出最大的数或最小的数。而其他的一些容器比如队列和栈,取出的顺序跟插入的顺序是有关的。

优先级队列的接口如下:

public class MaxPQ<Key extends Comparable<Key>> {
    MaxPQ();
    void insert(Key x);
    Key popMax();
    boolean isEmpty();
}

优先级队列的应用

  • 事件驱动的模拟
  • 数字运算
  • 数据压缩
  • 图的查找
  • 数论
  • 人工智能(A*查找)
  • 统计学
  • 操作系统(负载平衡)
  • 离散优化(背包问题)
  • 垃圾邮件过滤(贝叶斯垃圾过滤)

栈和队列是优先级队列的特殊情况

问题举例

现在有一个很大的txt文件,文件中包含了许多整数,文件中的数据量非常大,无法将整个文件读取到内存中。求该文件中最大的5个整数。

解答

这个问题就要用优先级队列进行求解,每次读取一行,将数据加入到优先级队列,如果队列的长度大于5,就删除最小的元素。当整个文件读取完毕时,优先级队列中剩余的元素就是要求的最大5个数。

复杂度

设需要在N个数据中求出最大的M个元素。

  • 如果用排序算法进行求解,时间复杂度是N logN,空间复杂度是N。
  • 如果用基础优先级队列进行求解,时间复杂度是N logM,空间复杂度为M。
  • 如果用堆的数据结构来做,那么时间复杂度是N logM,空间复杂度是M。
  • 理论上能达到最小的时间复杂度是N,空间复杂度是M。

使用堆的算法来求解该问题时,它的复杂度已经非常接近理论极限了。

代码

下面展示了最简单的优先级队列实现方法。插入的复杂度是1,删除的复杂度是N。

public class UnorderedPQ<Key extends Comparable<Key>> {
    private Key[] items;
    private int N;

    public UnorderedPQ(int capacity) {
        items = (Key[])new Comparable[capacity];
    }

    public void insert(Key item) {
        items[N] = item;
        N++;
    }

    public Key popMax() {
        // 找出最大的数字
        int max = 0;
        for(int i=1;i<N;i++){
            if(SortUtil.less(items[max], items[i])) {
                max=i;
            }
        }

        // 删除最大的数字
        Key result = items[max];
        SortUtil.exch(items, max, N-1); // 注意:这里是N-1,不是N
        N--;

        // 返回最大的数
        return result;
    }

    public boolean isEmpty() {
        return N==0;
    }
}

普林斯顿公开课 算法4-1:优先级队列API和基本实现

时间: 2024-08-09 02:17:32

普林斯顿公开课 算法4-1:优先级队列API和基本实现的相关文章

普林斯顿公开课 算法4-4:优先级队列的应用

问题 模拟N个运动的小球,小球之间可以发生弹性碰撞 建模 硬碟模型 N个运动的质点,它们之间能发生弹性碰撞,也可以和墙壁发生弹性碰撞 每个质点有明确的位置.速度.质量.半径 没有其他外力 模型应用范围 麦克斯韦:物体的温度和微粒速度分布的关系 爱因斯坦:解释花粉的布朗运动 基本思想 基本思想就是预测小球的碰撞时间,然后将碰撞时间作为优先级队列的关键字进行排序,每次从优先级队列中取出最小的元素,进行碰撞计算,然后重新预测碰撞时间. 这里面涉及到很多物理知识,代码就不演示了. 普林斯顿公开课 算法4

普林斯顿公开课 算法4-2:二叉堆

二叉树 介绍二叉堆之前首先介绍二叉树.二叉树有一个根节点,节点下又有两个子节点.完全二叉树是指一个二叉树树除了最底层,其他层都是完全平衡的. 完全二叉树最基本的性质就是它的高度是 floor(lgN). 二叉堆 二叉堆是完全二叉树的一种,每个节点对应一个数值,而且这个数值都大于等于它子节点的数值. 下图是一个二叉堆. 二叉堆的储存 由于二叉堆是完全二叉树,所以它可以用一个数组进行储存.所以不需要创建节点对象,再建立节点之间的连接.这样节省了很多开销. 用数组a[]表示一个二叉堆有以下特性: a[

普林斯顿公开课 算法1-10:并查集-优化的快速合并方法

应用 渗透问题 游戏中会用到. 动态连接 最近共同祖先 等价有限状态机 物理学Hoshen-Kopelman算法:就是对网格中的像素进行分块 Hinley-Milner多态类型推断 Kruskai最小生成树 Fortran等价语句编译 形态学开闭属性 Matlab中关于图像处理的bwlabel函数 渗透问题 一个N×N的矩阵,判断顶部和底部是否连通就是渗透问题. 下图中左侧的矩阵能渗透,右侧矩阵不能渗透. 渗透问题在电学.流体力学.社会交际中都有应用. 在游戏中可能需要生成一张地图,但是作为地图

普林斯顿公开课 算法1-11:并查集的应用

应用 渗透问题 游戏中会用到. 动态连接 最近共同祖先 等价有限状态机 物理学Hoshen-Kopelman算法:就是对网格中的像素进行分块 Hinley-Milner多态类型推断 Kruskai最小生成树 Fortran等价语句编译 形态学开闭属性 Matlab中关于图像处理的bwlabel函数 渗透问题 一个N×N的矩阵,判断顶部和底部是否连通就是渗透问题. 下图中左侧的矩阵能渗透,右侧矩阵不能渗透. 渗透问题在电学.流体力学.社会交际中都有应用. 在游戏中可能需要生成一张地图,但是作为地图

普林斯顿公开课 算法2-1:排序概述

目标 对所有类型的数据进行排序. 问题 排序函数如何知道比较的是哪种类型的数据呢? 回调函数 这时候就需要引入回调函数的概念了.回调函数就是将可执行的代码作为参数进行传递. 实现回调的方法 在Java中可以通过接口来实现,在C语言中可以通过函数指针来实现,C++中可以通过class-type functor,也就是重载操作符operator ()的类,在C#中可以使用Delegate委托,在Python/Perl/ML/javascript中可以直接传递函数. JDK中提供了Comparable

普林斯顿公开课 算法1-1:算法分析

为什么要分析算法 分析算法能够预測算法的性能,比較算法之间的优劣,保证算法的正确性,理解算法的理论基础. 成功算法的样例 离散傅立叶变换,假设使用暴力方法,那么算法的复杂度是是N^2,假设使用FFT高速傅立叶变换能够实现O(N logN)复杂度 N-body模拟:使用Barnes-hut算法能够将复杂度减少到N logN 顺便发一张N-body模拟的炫图 Barnes-Hut算法示意图 算法分析的步骤 观察问题的特征和想到得到的结果 依据观察结果提出如果 使用如果来预測可能发生的情况 检測预測结

普林斯顿公开课 算法1-2:观察

这章通过一个简单的例子,详细说明算法分析的步骤. 算法 问题 给定N个不同的整数,从中任意取出三个整数.请问有几种情况,使得取出的3个整数之和为0? 解法 可以使用暴力算法,代码如下: 1 2 3 4 5 6 7 8 9 for(int i=0;i<N;i++){     for(int j=0;j<N;j++){         for(int k=0;k<N;k++){             if(a[i]+b[i]+a[k]==0){                 count+

普林斯顿公开课 算法1-3:数学模型

本节主要通过建立数学模型,来计算算法的运行时间. 公式 算法的运行时间=所有操作的开销乘以操作的次数之和 开销 下表展示了各种操作所需要的时间(单位:纳秒) 整数加法 2.1 整数乘法 2.4 整数除法 5.4 浮点加法 4.6 浮点乘法 4.2 浮点除法 13.5 sin 91.3 arctan 129.0 举例 问题 计算数据中0的个数 代码 1 2 3 4 int count = 0; for (int i= 0; i < N; i++)     if (a[i] == 0)       

普林斯顿公开课 算法1-5:算法理论

本节主要讲解的是算法的复杂度. 算法性能 算法的性能分为三种: 最佳情况:计算时间最短的情况 最差情况:计算时间最长的情况 平均情况:随机输入的期望开销 以二分查找为例 最佳情况是1,因为第一次就有可能找到需要找的整数. 最差情况是logN 平均情况是logN 算法复杂度 算法复杂度用于定义问题的难度,另外也有助于开发最优化的算法,算法复杂度可以通过分析最坏情况来减少输入数据对算法性能的影响. 为了简化问题难度的表示方法,算法复杂度减少了算法分析的细节,忽略常数系数. 最优算法 所谓的最佳算法就