Heap和Heapify
最近复习数据结构,又回去再看塞神的课件,看到PriorityQueue的实现。自己也根据塞神的代码写一写。
下面使用Binary Heap实现了一个简单的 Max-oriented PriorityQueue。
- 这里Binary Heap我们使用的是array represetation,数组形式。
- 第0个元素我们留空,从第一个元素开始存储, 第一个元素也将是PQ里最大的元素。
- 特点是假如父节点位置是 k, 那么两个子节点的位置就是 2 * k 和 2 * k + 1。这样很方便计算,知道父节点很容易计算出子节点,知道子节点的位置也能立刻知道父节点的位置。
- Max Heap服从Max heap order, 即父节点的值永远大于等于子节点的值。 (但父节点的sibling可以不大于当前父节点的子节点值)
- 元素最好是implements Comparable[],否则我们还要另外写Comparator<>()
- 一开始的构造方法里,我们为简便使用了一个定长的数组。正常来说应该使用一个resizing array,以及一个load factor。
- 当load factor,也就是元素数 N / 数组长度 len = 0.75的时候,我们把数组扩容一倍,然后把之前的元素拷贝进去
- 当load factor < 0.25的时候,我们把数组减半,也要拷贝之前的元素。
- 主要的一些方法有insert(),peek(), delMax()以及isEmpty(),为了测试我也放入了一些其他方法,比如shuffle(), heapify(), 和heapSort(),下面一点点来分析各个方法。
- insert(): 每次添加元素的时候,我们都先增加PQ中元素的个数,即++N,然后把新元素x放在数组中新的这个位置上。接下来我们调用swim()方法来使PQ依然保持有序。 总复杂度是O(logn)
- swim(): 向上维护heap order。当我们发现,或者不确定数组中一个位置的元素是否符合Max heap order时,我们需要对这个位置的值进行一个swim()操作。只用考虑子节点和父节点,不用考虑sibling.
- 主要操作就是将这个子节点和其父节点进行比较,假如这个位置为k的子节点的值大于其父节点,我们交换这两个节点
- 继续比较交换后的子节点和其新的父节点, 这个可以通过 k /= 2来完成。
- 遍历在 k > 1的条件下进行。因为 k > 1的时候, k / 2最大就是1,也就是我们的最大节点
- 每次insert的时候我们可以使用swim()来保持heap order
- peek(): 我们可以直接返回最大节点elements[1],注意一些边界条件,或者这个节点不存在的时候抛出Exception
- delMax(): 删除最大节点是Max-heap的特色。 总复杂度 O(logn)
- 我们先交换最大节点和最后一个位置的节点,用N-- 将元素数N减少1, 并且将最大节点所在位置置为空 - elements[N + 1] = null。 这样可以避免loitering,避免垃圾回收机制收不到这个数组。
- 这时我们处在elements[1]位置上的元素有可能不满足Max heap order,我们执行 sink() 方法来进行处理。
- sink(): 向下维护heap order。 这时我们知道这个元素有可能和其两个子节点间都不满足Max heap order。我们在判断的时候要同时比较父节点和两个子节点间的大小。
- 假设当前父节点位置为k,那么两个可能的子节点位置为 2 * k 和 2 * k + 1。我们要先判断左子节点是否存在,也就是 2 * k 是否 <= N
- 在左子节点存在的条件下,我们设置 j = 2 * k,接下来我们判断右子节点是否存在,即 j是否 < N, 假如右子节点存在,我们比较左右子节点的大小,并且尝试更新j 为较大子节点的index值
- 接下来我们判断是否较大的子节点大于父节点的值, 假如为否,elements[j] < elements[k], 那么我们直接break
- 否则,我们交换 k 和 j - swap(k, j), 并且更新k = j, 继续下一个level的sink
- isEmpty(): pq是否为空,这是我们直接判断是否 N == 0
- swap(): 交换两个节点
- shuffle(): 这里使用了knuth shuffle。就是先用seed建立一个Random, 然后遍历数组的时候生成伪随机数,与当前index进行交换。 O(n)
- heapify(): 这里是指最大heapify。 我们只需要从 k = N / 2开始, 在k >= 1的条件下对 k 进行sink(), 然后k--就可以了。
- 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 testing
java.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 testing
for (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。
- heapify()方法: 可以看出我们的heapify方法基本没有变化,除了把N / 2变成了数组的长度 nums.length / 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的更多相关文章
- [LintCode]——目录
Yet Another Source Code for LintCode Current Status : 232AC / 289ALL in Language C++, Up to date (20 ...
- codevs 2830 蓬莱山辉夜
2830 蓬莱山辉夜 http://codevs.cn/problem/2830/ 题目描述 Description 在幻想乡中,蓬莱山辉夜是月球公主,居住在永远亭上,二次设定说她成天宅在家里玩电脑, ...
- codevs 1245 最小的N个和
1245 最小的N个和 http://codevs.cn/problem/1245/ 题目描述 Description 有两个长度为 N 的序列 A 和 B,在 A 和 B 中各任取一个数可以得到 N ...
- Python -- 堆数据结构 heapq - I love this game! - 博客频道 - CSDN.NET
Python -- 堆数据结构 heapq - I love this game! - 博客频道 - CSDN.NET Python -- 堆数据结构 heapq 分类: Python 2012-09 ...
- 算法&数据结构系列 -- 堆(优先队列)
前言 话说新开的博客十分好用... 所以,我打算开一个坑,名曰[算法系列]. 什么意思--从名字泥应该就猜得出来... 废话不多说,进入正文~~ 正文 原理 首先,堆是一颗棵二叉树.. 其次,堆是一棵 ...
- [Swift]LeetCode703. 数据流中的第K大元素 | Kth Largest Element in a Stream
Design a class to find the kth largest element in a stream. Note that it is the kth largest element ...
- 【LeetCode】23. Merge k Sorted Lists 合并K个升序链表
作者: 负雪明烛 id: fuxuemingzhu 个人博客:http://fuxuemingzhu.cn/ 个人公众号:负雪明烛 本文关键词:合并,链表,单链表,题解,leetcode, 力扣,Py ...
- Lintcode: Heapify && Summary: Heap
Given an integer array, heapify it into a min-heap array. For a heap array A, A[0] is the root of he ...
- 二叉堆(binary heap)
堆(heap) 亦被称为:优先队列(priority queue),是计算机科学中一类特殊的数据结构的统称.堆通常是一个可以被看做一棵树的数组对象.在队列中,调度程序反复提取队列中第一个作业并运行,因 ...
随机推荐
- 目前国内外主流的linux发行版本
1.linux其实是基于unix发展而来的,还有mac os也是类unix操作系统 2.目前主流的linux发行版本主要有:红帽系列(中国大陆,美洲地区,发源于美国),suse系列(欧洲地区流行,发源 ...
- 基于.net mvc的校友录(五、web.config对的配置以及filter实现的权限控制)
web.config配置文件 此文件是整个系统的配置中心,它告诉iis服务器本网站需要哪些运行时环境,需要哪些环境,将要进行哪些操作,开发人员也会将一个常量性的数据放在此配置中,以备系统全局调用.此文 ...
- 程序开发心理学阅读笔记——第II篇
作为社会行为的软件开发程序开发组->程序开发团队->程序开发项目1.要判断程序员的某个集体是否构成一支团队,要看其中的成员以何种方式相互协作,以共同开发软件产品.2.健康的团队要始终能够保 ...
- 1、android orm之greendao
前提:最近写android项目,android自带数据库api有点复杂,于是偷懒用了greendao.greendao好处自己查,这里不赘述,但是零基础的的我用起来还是费了3天的功夫,取之于网络,特在 ...
- 三门概率问题之C#版
前言: 早上看到一片关于三门问题的博客http://www.cnblogs.com/twocats/p/3440398.html,抱着该博客结论的怀疑态度用C#语言写了一些代码.实验证明该博客的结论是 ...
- [转载]关于android SDK安装Failed to fetch URL http://dl-ssl.google.com/android/repository/addons_list-1.xml出错
原文地址为:http://blog.csdn.net/springsky_/article/details/7442388 因为入行移动测试,所以很多测试环境的搭建.从中遇到了和这个GG同样的问题.怕 ...
- 802.11 wireless 二
802.11 wireless 2wireless spectrum(无线频谱)1.无线网络使用RF(射频)信号2.无线电也是电磁波3.频谱基于波长被划分,归为多个类型4.无线网络被归为微波段(mic ...
- JAVA Hashmap不能用基本的数据类型
今天开始学习Java... 转载:http://moto0421.iteye.com/blog/1143777 今天试了一下HahsMap, 采用如下形似定义 (这个下面是用了csdn的一位同仁的文章 ...
- CRC16校验
C++中的代码如下:传入字节(byte)数组引用和数组长度 unsigned short CTcpClient::Crc16(const char *pBuf, unsigned short nLen ...
- ios后台下载
http://www.cocoachina.com/industry/20131106/7304.html