最近复习数据结构,又回去再看塞神的课件,看到PriorityQueue的实现。自己也根据塞神的代码写一写。

下面使用Binary Heap实现了一个简单的 Max-oriented PriorityQueue。

  1. 这里Binary Heap我们使用的是array represetation,数组形式。

    1. 第0个元素我们留空,从第一个元素开始存储, 第一个元素也将是PQ里最大的元素。
    2. 特点是假如父节点位置是 k, 那么两个子节点的位置就是 2 * k 和 2 * k + 1。这样很方便计算,知道父节点很容易计算出子节点,知道子节点的位置也能立刻知道父节点的位置。
    3. Max Heap服从Max heap order, 即父节点的值永远大于等于子节点的值。           (但父节点的sibling可以不大于当前父节点的子节点值)
    4. 元素最好是implements Comparable[],否则我们还要另外写Comparator<>()
  2. 一开始的构造方法里,我们为简便使用了一个定长的数组。正常来说应该使用一个resizing array,以及一个load factor。
    1. 当load factor,也就是元素数 N / 数组长度 len = 0.75的时候,我们把数组扩容一倍,然后把之前的元素拷贝进去
    2. 当load factor < 0.25的时候,我们把数组减半,也要拷贝之前的元素。
  3. 主要的一些方法有insert(),peek(), delMax()以及isEmpty(),为了测试我也放入了一些其他方法,比如shuffle(), heapify(), 和heapSort(),下面一点点来分析各个方法。
    1. insert():  每次添加元素的时候,我们都先增加PQ中元素的个数,即++N,然后把新元素x放在数组中新的这个位置上。接下来我们调用swim()方法来使PQ依然保持有序。  总复杂度是O(logn)
    2. swim():   向上维护heap order。当我们发现,或者不确定数组中一个位置的元素是否符合Max heap order时,我们需要对这个位置的值进行一个swim()操作。只用考虑子节点和父节点,不用考虑sibling.
      1. 主要操作就是将这个子节点和其父节点进行比较,假如这个位置为k的子节点的值大于其父节点,我们交换这两个节点
      2. 继续比较交换后的子节点和其新的父节点, 这个可以通过 k /= 2来完成。
      3. 遍历在 k > 1的条件下进行。因为 k > 1的时候,  k / 2最大就是1,也就是我们的最大节点
      4. 每次insert的时候我们可以使用swim()来保持heap order
    3. peek():  我们可以直接返回最大节点elements[1],注意一些边界条件,或者这个节点不存在的时候抛出Exception
    4. delMax():  删除最大节点是Max-heap的特色。             总复杂度 O(logn)
      1. 我们先交换最大节点和最后一个位置的节点,用N-- 将元素数N减少1, 并且将最大节点所在位置置为空 -  elements[N + 1] = null。 这样可以避免loitering,避免垃圾回收机制收不到这个数组。
      2. 这时我们处在elements[1]位置上的元素有可能不满足Max heap order,我们执行 sink() 方法来进行处理。
    5. sink():  向下维护heap order。 这时我们知道这个元素有可能和其两个子节点间都不满足Max heap order。我们在判断的时候要同时比较父节点和两个子节点间的大小。
      1. 假设当前父节点位置为k,那么两个可能的子节点位置为 2 * k 和 2 * k + 1。我们要先判断左子节点是否存在,也就是 2 * k 是否 <= N
      2. 在左子节点存在的条件下,我们设置 j = 2 * k,接下来我们判断右子节点是否存在,即  j是否 < N, 假如右子节点存在,我们比较左右子节点的大小,并且尝试更新j 为较大子节点的index值
      3. 接下来我们判断是否较大的子节点大于父节点的值,  假如为否,elements[j] < elements[k], 那么我们直接break
      4. 否则,我们交换 k 和 j -  swap(k, j), 并且更新k  = j, 继续下一个level的sink
    6. isEmpty():  pq是否为空,这是我们直接判断是否  N == 0
    7. swap():  交换两个节点
    8. shuffle():  这里使用了knuth shuffle。就是先用seed建立一个Random, 然后遍历数组的时候生成伪随机数,与当前index进行交换。   O(n)
    9. heapify():  这里是指最大heapify。 我们只需要从  k = N / 2开始,  在k >= 1的条件下对 k 进行sink(), 然后k--就可以了。
    10. heapSort(): 堆排序, 这里我们先对数组进行heapify(), 然后在k > 1的条件下每次把最大元素交换到数组尾部,再对位置1的元素进行sink就可以了。   in-place  O(nlogn)。
public class MaxPQ {public Integer[] elements;public int N;public MaxPQ(int size) {elements = new Integer[size + 1];N = 0;            // index starts with 1
    }public void insert(Integer x) {elements[++N] = x;swim(N);}private void swim(int k) {while (k > 1 && elements[k] > elements[k / 2]) {swap(k, k / 2);k /= 2;}}public Integer delMax() {Integer max = elements[1];swap(1, N--);elements[N + 1] = null;sink(1);return max;}private void sink(int k) {while (2 * k <= N) {int j = 2 * k;if (j < N && elements[j] < elements[j + 1]) {  j++;}if (elements[j] < elements[k]) {break;}swap(k, j);k = j;}}public Integer peek() {return elements[1];}public boolean isEmpty() {return N == 0;}private void swap(int i, int j) {Integer tmp = elements[i];elements[i] = elements[j];elements[j] = tmp;}public void shuffle() {            // for testingjava.util.Random rand = new java.util.Random(System.currentTimeMillis());for (int i = 1; i <= N; i++) {int r = 1 + rand.nextInt(i);swap(i, r);}        }public void heapify() {            // for testingfor (int k = N / 2; k >= 1; k--) {sink(k);}}public void heapSort() {heapify();int n = N;while (n > 1) {swap(1, n--);sink(1);}}
}

上面是用Binary heap设计一个 Max-oriented Priority Queue, 数组是1-based。 假如遇到面试官问怎么heapify怎么办?  下面我们就对上面代码进行少许改动,变为0-based,可以直接对数组进行max - heapify。

  1. heapify()方法: 可以看出我们的heapify方法基本没有变化,除了把N / 2变成了数组的长度 nums.length / 2
  2. sink()方法 : 这里我们要注意一下边界条件。 先设置len = nums.length,这里len就相当于之前的N, 然后再进行比较的时候,我们要把每次的 j 都减1,从1-based改变为 0-based,其他代码都不需要改变
    public static void heapify(int[] nums) {if (nums == null) {return;}for (int k = nums.length / 2; k >= 1; k--) {sink(nums, k);}}private static void sink(int[] nums, int k) {int len = nums.length;while (2 * k <= len) {int j = 2 * k;if (j < len && nums[j - 1] < nums[j]) {j++;}if (nums[k - 1] > nums[j - 1]) {break;}swap(nums, k - 1, j - 1);k = j;}}private static void swap(int[] nums, int i, int j) {int tmp = nums[i];nums[i] = nums[j];nums[j] = tmp;}

Test Client:

public static void main(String[] args) {int len = 10;int[] nums = new int[len];for (int i = 0; i < len; i++) {nums[i] = i + 1; }shuffle(nums);for (int i : nums) {System.out.print(i + " ");}heapify(nums);System.out.println();for (int i : nums) {System.out.print(i + " ");}}

Reference:

http://algs4.cs.princeton.edu/24pq/

Heap和Heapify相关推荐

  1. heap python_数据结构-堆(Heap) Python实现

    堆(Heap)可以看成近似完全二叉树的数组,树中每个节点对应数组中一个元素.除了最底层之外,该树是完全充满的,最底层是从左到右填充的. 堆包括最大堆和最小堆:最大堆的每一个节点(除了根结点)的值不大于 ...

  2. python之堆heapq模块

    python之堆heapq模块 堆是一种特殊的树形结构,通常我们所说的堆的数据结构指的是完全二叉树,并且根节点的值小于等于该节点所有子节点的值. 堆是非线性的树形的数据结构,有两种堆,最大堆与最小堆. ...

  3. 时间复杂度为on的排序算法_排序算法amp;时间复杂度计算

    对于排序算法而言,有几个重要的点: 理解此种排序算法是怎么运行的 理解算法的时间复杂度与空间复杂度计算 递推公式(关乎时间复杂度的计算) 递推公式主要为以下的形式(递归使用的复杂度也这么算): 具体推 ...

  4. Python heapq库的用法介绍

    Python heapq库的用法介绍 一.heapq库简介 heapq 库是Python标准库之一,提供了构建小顶堆的方法和一些对小顶堆的基本操作方法(如入堆,出堆等),可以用于实现堆排序算法. 堆是 ...

  5. 剑指offer全书题解 (Python)【更新完毕】

    文章目录 2 实现 Singleton 模式 3 找出数组中重复的数字 3.2 不修改数组找出重复的数字 4 二维数组中的查找 5 替换空格 6 从尾到头打印链表 7 重建二叉树 8 二叉树的下一个节 ...

  6. 最大堆和最小堆和平衡二叉树_最小堆二叉树

    最大堆和最小堆和平衡二叉树 A Min Heap Binary Tree is a Binary Tree where the root node has the minimum key in the ...

  7. python中heapq的库是什么_详解Python中heapq模块的用法

    详解Python中heapq模块的用法 来源:中文源码网    浏览: 次    日期:2018年9月2日 [下载文档:  详解Python中heapq模块的用法.txt ] (友情提示:右键点上行t ...

  8. python中heapq的库是什么_Python中heapq模块的用法

    heapq 模块提供了堆算法.heapq是一种子节点和父节点排序的树形数据结构.这个模块提供heap[k] <= heap[2*k+1] and heap[k] <= heap[2*k+2 ...

  9. Python -- 堆数据结构 heapq - I love this game! - 博客频道 - CSDN.NET

    Python -- 堆数据结构 heapq - I love this game! - 博客频道 - CSDN.NET Python -- 堆数据结构 heapq - I love this game ...

最新文章

  1. 对付感冒的十二种偏方
  2. Windows Server 2008 R2
  3. 赠书|大厂面试喜欢考算法,该怎么破?
  4. php env 没有值,PHP DOTENV无法加载env变种
  5. hdu3579(中国剩余问题经典)
  6. Python:isinstance()和type()
  7. python socket connection_Python socket.create_connection方法代码示例
  8. 资源分享:一千张高清头像图片免费分享,适用于网站app程序使用!
  9. 用粉红噪声煲机_虾米歌单 | 【科学煲耳机】(白噪音 粉红噪音 无损) - 虾米音乐...
  10. 报错Could not find module ‘D:\Anaconda\lib\site-packages\scipy\.libs\
  11. python公历转农历_Python农历公历转换
  12. 苹果库乐队怎么玩_苹果手机如何自定义铃声?这个方法最简单。
  13. 安搭Share:霜降来,寒意到
  14. Axure高级功能(变量、动态面板[轮播图]、中继器)
  15. 田园项目tabber标签栏使用
  16. CPP头文件中不应包含using声明
  17. 为什么计算机编程全用英语,为什么所有编程都是英文的?为什么不能中文程序?...
  18. java JNI介绍
  19. 【带你敲】演讲比赛流程管理系统
  20. 单页面应用在微信服务号下的登录流程

热门文章

  1. SQL注入进阶练习(一)一些进阶的注入方法
  2. Django创建数据库(Django数据库字段类型)
  3. UE4中的GameMode、GameState、GameInstance
  4. 【编程初学者】创建自己的开源项目1-创建远程代码仓库
  5. 3D打印技术之切片引擎(7)
  6. 2021年第十二届蓝桥杯省赛B组(C/C++)个人题解
  7. OpenSSL BIO 自我扫盲
  8. Water Research | 南科大夏雨组揭示Anammox菌群微米级空间异质性和保守互作
  9. QGIS二次开发:加载XYZ Tiles形式的瓦片地图
  10. Python地理数据处理 二:Python基础知识