索引式优先队列(indexed priority queue)
为了达到O(ElogV)的效率,需要对普利姆算法进行eager实现。
如果我们用java来做,jdk当中的priorityQueue并不能满足我们的要求。
因为我们需要进行一个对索引元素降key的操作(decrease-key).
/**
* 将索引所关联的key降到newKey
*
* @param index 索引
* @param newKey 新的key
*/
public void decreaseKey(int index, E newKey) {
if (index < 0 || index >= queue.length)
throw new IndexOutOfBoundsException();
if (newKey == null)
throw new NullPointerException();
if (!contains(index))
throw new NoSuchElementException("指定的索引不存在!");
if (this.comparator() != null) {
if (this.comparator().compare((E) keys[index], newKey) <= 0)
throw new IllegalArgumentException("指定的key必须小于原索引关联的key!");
} else {
Comparable<? super E> key = (Comparable<? super E>) getKeyOf(index);
if (key.compareTo(newKey) <= 0)
throw new IllegalArgumentException("指定的key必须小于原索引关联的key!");
}
keys[index] = newKey;
siftUp(getPositionOf(index));
}
由于需要知道优先队列中元素的索引以支持外部索引式访问,我们将对priorityQueue中的Queue建立索引,并将此索引当作二叉堆实际存储的元素,而原来的元素我们通过索引表来访问它.
/**
* 索引所关联的keys
*/
private transient Object[] keys;
/**
* 建立一个从索引在二叉堆中的位置到索引的映射
*/
private transient int[] queue;
这里还有一个问题,就是在进行上浮(siftUp)和下沉(siftDown)时,需要对堆中的元素和位置进行互访,我们还需要建立一个索引到位置的倒排,或者索引到位置的一个映射.
/**
* 建立一个从索引到其在二叉堆中位置的映射,下标是关联key的索引
*/
private transient int[] postQueue;
这样我们就可以按下面的方式访问二叉堆中的索引、它在二叉堆中的位置、以及它关联的key:
/**
* 返回给定位置的索引
*
* @param position 二叉堆中的位置
* @return 给定位置的索引
*/
private int getIndexOf(int position) {
return queue[position];
}
/**
* 返回给定索引在二叉堆中的位置
*
* @param index 索引
* @return 给定索引在二叉堆中的位置
*/
private int getPositionOf(int index) {
return postQueue[index];
}
/**
* 返回给定索引所关联的key
*
* @param index 索引
* @return 索引所关联的key
*/
private E getKeyOf(int index) {
return (E) keys[index];
}
而交换元素时,需要先交换索引,然后再维护索引到位置的映射:
/**
* 按位置交换
*
* @param x 一个元素的位置
* @param y 另一个元素的位置
*/
private void exchangeByPosition(int x, int y) {
int tmp = queue[x];
queue[x] = queue[y];
queue[y] = tmp;
postQueue[queue[x]] = x;
postQueue[queue[y]] = y;
}
这样,我们的上浮操作看起来是这样的:
private void siftUpComparable(int k) {
Comparable<? super E> key = (Comparable<? super E>) keys[getIndexOf(k)];
while (k > 0) {
int parent = (k - 1) >>> 1;
Object e = keys[queue[parent]];
if (key.compareTo((E) e) >= 0)
break;
exchangeByPosition(k, parent);
k = parent;
}
}
而下沉操作就变成了这样:
private void siftDownUsingComparator(int k) {
E x = getKeyOf(getIndexOf(k));
int half = size >>> 1;
while (k < half) {
//假设作孩子是least
int child = (k << 1) + 1;
Object c = keys[child];
int right = child + 1;
//如果右孩子更小,那就和右孩子比较
if (right < size &&
comparator.compare((E) c, getKeyOf(getIndexOf(right))) > 0)
c = getKeyOf(getIndexOf(child = right));
//当元素不大于它的所有孩子时停止
if (comparator.compare(x, (E) c) <= 0)
break;
//否则交换元素和它最小的孩子
exchangeByPosition(k, child);
//继续下沉
k = child;
}
}
既然加入了对元素的索引支持,那么入队操作就变成了这样:
/**
* 将index关联的key加入队列
*
* @param index 索引
* @param key key
* @return
*/
public boolean offer(int index, E key) {
if (index < 0)
throw new IndexOutOfBoundsException();
if (key == null)
throw new NullPointerException();
int last = size;
//是否需要扩容
if (last >= keys.length)
grow(last + 1);
size = last + 1;
//首先将元素加入到队尾,然后从队尾上浮,直到满足堆的不变性
//index到position的映射
postQueue[index] = last;
//position到index的映射
queue[last] = index;
//保存index关联的key
keys[index] = key;
if(last == 0)
return true;
//上浮
siftUp(last);
return true;
}
相应的,出队操作也要修改:
/**
* 返回最优先的元素并在队列中删除此元素
*
* @return 最优先的元素, 如果为负,表示队列已空
*/
public int poll() {
if (size == 0)
return -1;
int tail = --size;
int head = getIndexOf(0);
if(tail != 0){
exchangeByPosition(0, tail);
siftDown(0);
}
postQueue[head] = -1;
//队尾的元素经过交换后就是之前的队头,现在可以删除了
keys[getIndexOf(tail)] = null;
queue[tail] = -1;
return head;
}
下面是对索引式优先队列的一个完整实现,支持最大优先队列和最小优先队列:
import java.util.*;
/**
* Created by 浩然 on 4/19/15.
* 索引式优先队列
* <p/>
* 建立目的:
* 在实现普利姆算法时,需要用优先队列来优化最轻边的查找.
* 而jdk中的优先队列只满足lazy实现,如果要作eager实现,需实现一个decrease-key操作.
* <p/>
* 参考:
* 1.普林斯顿大学 algorithms 4th edition.
* 2.jdk priorityQueue.
* <p/>
* 说明:
* 1.队头是最least的元素
* 2.可通过自定义Comparable或Comparator实现最大优先、最小优先队列.
* 3.假设是最小优先队列,堆的不变性是指,任何插入、删除、出队、入队的操作都满足以下性质:
* 任何一个父元素的key都不大于子元素的key
* 而对于最大优先队列,则需满足任何一个父元素的key都不小于子元素的key
*/
public class IndexPriorityQueue<E> {
private static final int DEFAULT_INITIAL_CAPACITY = 11;
/**
* 索引所关联的keys
* 以O(1)的时间找出给定索引所关联的key
*/
private transient Object[] keys;
//建立下面两个辅助字段的目的:
//1:以O(1)的时间找到给定索引的位置
//2:以O(1)的时间找到给定位置的索引
/**
* 建立一个从索引在二叉堆中的位置到索引的映射,下标就是索引在二叉堆中的位置
*/
private transient int[] queue;
/**
* 建立一个从索引到其在二叉堆中位置的映射,下标是关联key的索引
*/
private transient int[] postQueue;
private int size = 0;
private final Comparator<? super E> comparator;
public IndexPriorityQueue() {
this(DEFAULT_INITIAL_CAPACITY, null);
}
public IndexPriorityQueue(int initialCapacity) {
this(initialCapacity, null);
}
public IndexPriorityQueue(int initialCapacity,
Comparator<? super E> comparator) {
if (initialCapacity < 1)
throw new IllegalArgumentException();
this.keys = new Object[initialCapacity];
this.queue = new int[initialCapacity];
this.postQueue = new int[initialCapacity];
for (int i = 0; i < postQueue.length; i++) {
postQueue[i] = -1;
}
this.comparator = comparator;
}
private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;
/**
* 扩容
*
* @param minCapacity 所需的最小容量
*/
private void grow(int minCapacity) {
int oldCapacity = keys.length;
int newCapacity = oldCapacity + ((oldCapacity < 64) ?
(oldCapacity + 2) :
(oldCapacity >> 1));
if (newCapacity - MAX_ARRAY_SIZE > 0)
newCapacity = hugeCapacity(minCapacity);
keys = Arrays.copyOf(keys, newCapacity);
queue = Arrays.copyOf(queue, newCapacity);
postQueue = Arrays.copyOf(postQueue, newCapacity);
}
private static int hugeCapacity(int minCapacity) {
if (minCapacity < 0)
throw new OutOfMemoryError();
return (minCapacity > MAX_ARRAY_SIZE) ?
Integer.MAX_VALUE :
MAX_ARRAY_SIZE;
}
/**
* 返回队列是否为空
*
* @return 队列空则返回真,否则返回假
*/
public boolean isEmpty() {
return size == 0;
}
/**
* 将index关联的key加入队列
*
* @param index 索引
* @param key key
* @return
*/
public boolean offer(int index, E key) {
if (index < 0)
throw new IndexOutOfBoundsException();
if (key == null)
throw new NullPointerException();
int last = size;
//是否需要扩容
if (last >= keys.length)
grow(last + 1);
size = last + 1;
//首先将元素加入到队尾,然后从队尾上浮,直到满足堆的不变性
//index到position的映射
postQueue[index] = last;
//position到index的映射
queue[last] = index;
//保存index关联的key
keys[index] = key;
if(last == 0)
return true;
//上浮
siftUp(last);
return true;
}
/**
* 返回最优先元素,并且不删除此元素
*
* @return
*/
public int peek() {
if (size == 0)
return -1;
return getIndexOf(0);
}
/**
* 队列中是否包含指定的元素
*
* @param index 索引
* @return 如果指定的索引在队列中,返回真,否则返回假
*/
public boolean contains(int index) {
return getPositionOf(index) != -1;
}
/**
* 队列的长度
*
* @return
*/
public int size() {
return size;
}
/**
* 清空队列
*/
public void clear() {
for (int i = 0; i < size; i++) {
if (postQueue[i] < 0)
continue;
int index = getIndexOf(i);
keys[index] = null;
postQueue[index] = -1;
queue[i] = -1;
}
size = 0;
}
/**
* 返回最优先的元素并在队列中删除此元素
*
* @return 最优先的元素, 如果为负,表示队列已空
*/
public int poll() {
if (size == 0)
return -1;
int tail = --size;
int head = getIndexOf(0);
if(tail != 0){
exchangeByPosition(0, tail);
siftDown(0);
}
postQueue[head] = -1;
//队尾的元素经过交换后就是之前的队头,现在可以删除了
keys[getIndexOf(tail)] = null;
queue[tail] = -1;
return head;
}
private int remove(int index) {
assert index >= 0 && index < size;
if (!contains(index))
return -1;
int s = --size;
int position = getPositionOf(index);
//将堆中最后一个元素和要删除的元素交换
exchangeByPosition(position, s);
siftUp(position);
siftDown(position);
//help gc
keys[index] = null;
//标记在堆中已无此元素
postQueue[index] = -1;
return -1;
}
/**
* 上浮元素
*
* @param k 上浮开始的位置
*/
private void siftUp(int k) {
if (comparator != null)
siftUpUsingComparator(k);
else
siftUpComparable(k);
}
private void siftUpComparable(int k) {
Comparable<? super E> key = (Comparable<? super E>) keys[getIndexOf(k)];
while (k > 0) {
int parent = (k - 1) >>> 1;
Object e = keys[queue[parent]];
if (key.compareTo((E) e) >= 0)
break;
exchangeByPosition(k, parent);
k = parent;
}
}
private void siftUpUsingComparator(int k) {
E key = (E) keys[getIndexOf(k)];
while (k > 0) {
int parent = (k - 1) >>> 1;
Object e = keys[queue[parent]];
//如果key不小于其父节点的key,break
if (comparator.compare(key, (E) e) >= 0)
break;
//否则交换current、parent处的元素
exchangeByPosition(k, parent);
//继续上浮
k = parent;
}
}
/**
*
* @param k
*/
private void siftDown(int k) {
if (comparator != null)
siftDownUsingComparator(k);
else
siftDownComparable(k);
}
/**
* 按位置交换
*
* @param x 一个元素的位置
* @param y 另一个元素的位置
*/
private void exchangeByPosition(int x, int y) {
int tmp = queue[x];
queue[x] = queue[y];
queue[y] = tmp;
postQueue[queue[x]] = x;
postQueue[queue[y]] = y;
}
private void siftDownComparable(int k) {
Comparable<? super E> key = (Comparable<? super E>) getKeyOf(getIndexOf(k));
int half = size >>> 1;
while (k < half) {
int child = (k << 1) + 1;
Object c = getKeyOf(getIndexOf(child));
int right = child + 1;
if (right < size &&
((Comparable<? super E>) c).compareTo(getKeyOf(getIndexOf(right))) > 0)
c = getKeyOf(getIndexOf(child = right));
if (key.compareTo((E) c) <= 0)
break;
exchangeByPosition(k, child);
k = child;
}
}
private void siftDownUsingComparator(int k) {
E x = getKeyOf(getIndexOf(k));
int half = size >>> 1;
while (k < half) {
//假设作孩子是least
int child = (k << 1) + 1;
Object c = keys[child];
int right = child + 1;
//如果右孩子更小,那就和右孩子比较
if (right < size &&
comparator.compare((E) c, getKeyOf(getIndexOf(right))) > 0)
c = getKeyOf(getIndexOf(child = right));
//当元素不大于它的所有孩子时停止
if (comparator.compare(x, (E) c) <= 0)
break;
//否则交换元素和它最小的孩子
exchangeByPosition(k, child);
//继续下沉
k = child;
}
}
/**
* 将索引所关联的key降到newKey
*
* @param index 索引
* @param newKey 新的key
*/
public void decreaseKey(int index, E newKey) {
if (index < 0 || index >= queue.length)
throw new IndexOutOfBoundsException();
if (newKey == null)
throw new NullPointerException();
if (!contains(index))
throw new NoSuchElementException("指定的索引不存在!");
if (this.comparator() != null) {
if (this.comparator().compare((E) keys[index], newKey) <= 0)
throw new IllegalArgumentException("指定的key必须小于原索引关联的key!");
} else {
Comparable<? super E> key = (Comparable<? super E>) getKeyOf(index);
if (key.compareTo(newKey) <= 0)
throw new IllegalArgumentException("指定的key必须小于原索引关联的key!");
}
keys[index] = newKey;
siftUp(getPositionOf(index));
}
/**
* 将索引所关联的key升到newKey
*
* @param index 索引
* @param newKey 新的key
*/
public void increaseKey(int index, E newKey) {
if (index < 0 || index >= queue.length)
throw new IndexOutOfBoundsException();
if (newKey == null)
throw new NullPointerException();
if (!contains(index))
throw new NoSuchElementException("指定的索引不存在!");
if (this.comparator() != null) {
if (this.comparator().compare((E) keys[index], newKey) >= 0)
throw new IllegalArgumentException("指定的key必须大于原索引关联的key!");
} else {
Comparable<? super E> key = (Comparable<? super E>) getKeyOf(index);
if (key.compareTo(newKey) >= 0)
throw new IllegalArgumentException("指定的key必须大于原索引关联的key!");
}
keys[index] = newKey;
siftDown(getPositionOf(index));
}
/**
* 调整队列以保证堆的不变性
*/
private void heapify() {
for (int i = (size >>> 1) - 1; i >= 0; i--)
siftDown(i);
}
public Comparator<? super E> comparator() {
return comparator;
}
/**
* 返回给定位置的索引
*
* @param position 二叉堆中的位置
* @return 给定位置的索引
*/
private int getIndexOf(int position) {
return queue[position];
}
/**
* 返回给定索引在二叉堆中的位置
*
* @param index 索引
* @return 给定索引在二叉堆中的位置
*/
private int getPositionOf(int index) {
return postQueue[index];
}
/**
* 返回给定索引所关联的key
*
* @param index 索引
* @return 索引所关联的key
*/
private E getKeyOf(int index) {
return (E) keys[index];
}
}
索引式优先队列(indexed priority queue)的更多相关文章
- 优先队列(Priority Queue)
优先队列(Priority Queue) A priority queue must at least support the following operations: insert_with_pr ...
- 优先队列 :Priority Queue
PriorityQueue是从JDK1.5开始提供的新的数据结构接口,它是一种基于优先级堆的极大优先级队列.优先级队列是不同于先进先出队列的另一种队列. 每次从队列中取出的是具有最高优先权的元素.如果 ...
- 《Algorithms 4th Edition》读书笔记——2.4 优先队列(priority queue)-Ⅴ
命题Q.对于一个含有N个元素的基于堆叠优先队列,插入元素操作只需要不超过(lgN + 1)次比较,删除最大元素的操作需要不超过2lgN次比较. 证明.由命题P可知,两种操作都需要在根节点和堆底之间移动 ...
- 第二十八篇 玩转数据结构——堆(Heap)和有优先队列(Priority Queue)
1.. 优先队列(Priority Queue) 优先队列与普通队列的区别:普通队列遵循先进先出的原则:优先队列的出队顺序与入队顺序无关,与优先级相关. 优先队列可以使用队列的接口,只是在 ...
- Algorithms - Priority Queue - 优先队列
Priority queue - 优先队列 相关概念 Priority queue优先队列是一种用来维护由一组元素构成的集合S的数据结构, 其中的每一种元素都有一个相关的值,称为关键字(key). 一 ...
- STL之heap与优先级队列Priority Queue详解
一.heap heap并不属于STL容器组件,它分为 max heap 和min heap,在缺省情况下,max-heap是优先队列(priority queue)的底层实现机制.而这个实现机制中的m ...
- [置顶] ※数据结构※→☆线性表结构(queue)☆============优先队列 链式存储结构(queue priority list)(十二)
优先队列(priority queue) 普通的队列是一种先进先出的数据结构,元素在队列尾追加,而从队列头删除.在优先队列中,元素被赋予优先级.当访问元素时,具有最高优先级的元素最先删除.优先队列具有 ...
- 《Algorithms 4th Edition》读书笔记——2.4 优先队列(priority queue)-Ⅳ
2.4.4 堆的算法 我们用长度为 N + 1的私有数组pq[]来表示一个大小为N的堆,我们不会使用pq[0],堆元素放在pq[1]至pq[N]中.在排序算法中,我们只能通过私有辅助函数less()和 ...
- 《Algorithms 4th Edition》读书笔记——2.4 优先队列(priority queue)-Ⅰ
许多应用程序都需要处理有序的元素,但不一定要求他们全部有序,或者是不一定要以此就将他们排序.很多情况下我们会手机一些元素,处理当前键值最大的元素,然后再收集更多的元素,再处理当前键值最大的元素.如此这 ...
随机推荐
- java基础29 迭代器 listIterator() 及各种遍历集合的方法
listIterator() 迭代器包含了 Iterator() 迭代器中的所有方法. 1.ListIterator的常用方法 hasPrevious() :判断是否还有上一个元素,有则返回true ...
- Html5和Css3扁平化风格网页
前言 扁平化概念的核心意义 去除冗余.厚重和繁杂的装饰效果.而具体表现在去掉了多余的透视.纹理.渐变以及能做出3D效果的元素,这样可以让“信息”本身重新作为核心被凸显出来.同时在设计元素上,则强调了抽 ...
- drools7 (四、FactHandle 介绍)
先看代码 Base.java package cn.xiaojf.drools7.base; import org.apache.commons.lang3.StringUtils; import o ...
- 在ASP.NET Web API和ASP.NET Web MVC中使用Ninject
先附上源码下载地址 一.准备工作 1.新建一个名为MvcDemo的空解决方案 2.新建一个名为MvcDemo.WebUI的空MVC应用程序 3.使用NuGet安装Ninject库 二.在ASP.N ...
- 【POJ】1819.Disks
博客园的话插链接链接都是凉的= = 题解 我理解成能不能看到这个圆,除了最后几个圆特殊以外都是等价的,然而我凉了,因为我把圆当成线段来处理,但是,有可能一个圆完全被遮住了,还有一个缝隙,就WA了 计算 ...
- 包装印刷行业裕同集团&易普优APS项目顺利验收!
裕同集团&易普优APS项目于2017年7月启动,2018年1月上线,2018年5月初项目顺利验收!历时十个月,龙岗作为裕同集团APS的先锋试点项目,同时也是业务最复杂的分公司,双方联合团队紧密 ...
- bzoj 1233
先要了解一个结论,在多种可行的堆叠方案中,至少有一种能使层数最高的方案同时使得底边最短.即底边最短的,层数一定最高. dp[ i ] = min(sum[j - 1] - sum[i - 1]) j ...
- Tomcat基于MSM+Memcached实现Session共享
简述 上一篇文章,分别演示了session sticky 和 session cluster来实现会话保持的问题,但是它们缺点都不少,实际中用的很少,所以这篇文章我们还是通过Tomcat来演示一下实际 ...
- 006 ajax验证用户名
1.大纲 2.index.jsp <%@ page language="java" contentType="text/html; charset=UTF-8&qu ...
- Eclipse反编译插件的安装
步骤: 1.已经安装了Eclipse,如我的Eclipse目录:C:\Programs\JAVA\eclipse 2.反编译插件包:eclipse 反编译插件 jad 3.3.0.zip 3.解压反编 ...