优先队列
不仅仅是“先进先出”,而是可以插队。
优先级、关键码(Key)、全序关系与优先队列
将关键码定义为任意对象,必须建立一种统一的、相容的形式,以支持不同对象之间的比较,确定优先级。 实际上,作为优先队列的一个基本要求,在关键码之间必须能够定义某种全序关系(Total order relation)。 具体来说,任何两个关键码都必须能够比较“大小”。
举个例子:如果将这种全序关系用“≤”表示,则该关系还必须满足以下三条性质:
- 自反性:对于任一关键码 k,都有 k ≤ k
- 反对称性:若k1 ≤ k2且k2 ≤ k1,则k1 = k2
- 传递性:若k1 ≤ k2且k2 ≤ k3,则k1 ≤ k3
所谓的优先队列也是对象的一种容器,只不过其中的每个对象都拥有一个关键码,在它们的关 键码之间存在满足上述性质的某种全序关系“≤”。关键码可以是在对象插入优先队列时被人为赋予 的,也可能就是对象本身具有的某一属性。
-
关键码:对象
-
全序关系:if(){}else(){}
关键码通过全序关系确认优先级排成优先队列。
条目(Entry)
所谓一个条目(Entry),就是由一个对象及 其关键码合成的一个对象,它反映和记录了二者之间的关联关系。
这样,通过将条目对象作为优先队列的元素,即可记录和维护原先对象与其关键码之间的关联关系。
/**
* <b>Description:</b> 引入条目这一概念,旨在解决上面的前一个问题。
* 所谓一个条目(Entry),就是由一个对象及其关键码合成的一个对象,它反映和记录了二者之间的关联关系。
* 这样,通过将条目对象作为优先队列的元素,即可记录和维护原先对象与其关键码之间的关联关系。
* @author tongson
*/
public interface Entry {
/**
* 取条目的关键码
*
* @return
*/
public Object getKey();
/**
* 修改条目的关键码,返回此前存放的关键码
*
* @param k
* @return
*/
public Object setKey(Object k);
/**
* 取条目的数据对象
*
* @return
*/
public Object getValue();
/**
* 修改条目的数据对象,返回此前存放的数据对象
*
* @param v
* @return
*/
public Object setValue(Object v);
}
复制代码
public class EntryDefault implements Entry {
protected Object key;
protected Object value;
/**************************** 构造函数 ****************************/
public EntryDefault(Object k, Object v) {
key = k;
value = v;
}
/**************************** Entry接口方法 ****************************/
/**
* 取条目的关键码
*
* @return
*/
@Override
public Object getKey() {
return key;
}
/**
* 修改条目的关键码,返回此前存放的关键码
*
* @param k
* @return
*/
@Override
public Object setKey(Object k) {
Object oldK = key;
key = k;
return oldK;
}
/**
* 取条目的数据对象
*
* @return
*/
@Override
public Object getValue() {
return value;
}
/**
* 修改条目的数据对象,返回此前存放的数据对象
*
* @param v
* @return
*/
@Override
public Object setValue(Object v) {
Object oldV = value;
value = v;
return oldV;
}
}
复制代码
比较器
基于某种 Comparable 接口实现一个关键码类,并将所有通常的比较方法封装起来,以支持关键码之间的比较。
/**
* 基于某种 Comparable 接口实现一个关键码类,并将所有通常的比较方法封装起来,以支持关键码之间的比较。
*/
public interface Comparator {
/**
* 若a>(=或<)b,返回正数、零或负数
*
* @param a
* @param b
* @return
*/
public int compare(Object a, Object b);
}
复制代码
/**
* Comparable对象的默认比较器
*/
public class ComparatorDefault implements Comparator {
public ComparatorDefault() {
}
@Override
public int compare(Object a, Object b) throws ClassCastException {
return ((Comparable) a).compareTo(b);
}
}
复制代码
优先队列(interface)
public interface PQueue {
/**统计优先队列的规模
*
* @return
*/
public int getSize();
/**判断优先队列是否为空
*
* @return
*/
public boolean isEmpty();
/**若Q非空,则返回其中的最小条目(并不删除);否则,报错
*
* @return
* @throws ExceptionPQueueEmpty
*/
public Entry getMin() throws ExceptionPQueueEmpty;
/**将对象obj与关键码k合成一个条目,将其插入Q中,并返回该条目
*
* @param key
* @param obj
* @return
* @throws ExceptionKeyInvalid
*/
public Entry insert(Object key, Object obj) throws ExceptionKeyInvalid;
/**若Q非空,则从其中摘除关键码最小的条目,并返回该条目;否则,报错
*
* @return
* @throws ExceptionPQueueEmpty
*/
public Entry delMin() throws ExceptionPQueueEmpty;
}
复制代码
排序器(interface)
/**
* <b>Description:</b> 排序器接口 <br>
*/
public interface Sorter {
void sort(Sequence S);
}
复制代码
基于优先队列的排序器
/**
* <b>Description:</b> 基于优先队列的排序器 <br>
*/
public class SorterPQueue implements Sorter {
private Comparator C;
public SorterPQueue() {
this(new ComparatorDefault());
}
public SorterPQueue(Comparator comp) {
C = comp;
}
@Override
public void sort(Sequence S) {
//为批处理建立优先队列而准备的序列
Sequence T = new SequenceDLNode();
//构建序列T
while (!S.isEmpty()) {
//逐一取出S中各元素
Object e = S.removeFirst();
//用节点元素本身作为关键码
T.insertLast(new EntryDefault(e, e));
}
// PQueue pq = new PQueueUnsortedList(C, T);
// PQueue pq = new PQueueSortedList(C, T);
PQueue pq = new PQueueHeap(C, T);
//从优先队列中不断地
while (!pq.isEmpty()) {
//取出最小元素,插至序列末尾
S.insertLast(pq.delMin().getValue());
System.out.println("\t:\t" + S.last().getElem());
}
}
}
复制代码
用向量实现优先队列
用列表实现优先队列
基于无序列表的实现及分析
/**
* <b>Description:</b> 基于无序列表的实现及分析 <br>
*/
public class PQueueUnsortedList implements PQueue {
private List L;
private Comparator C;
/**
* 构造方法(使用默认比较器)
*/
public PQueueUnsortedList() {
this(new ComparatorDefault(), null);
}
/**
* 构造方法(使用指定比较器)
*
* @param c
*/
public PQueueUnsortedList(Comparator c) {
this(c, null);
}
/**
* 构造方法(使用指定初始元素)
*
* @param s
*/
public PQueueUnsortedList(Sequence s) {
this(new ComparatorDefault(), s);
}
/**
* 构造方法(使用指定比较器和初始元素)
*
* @param c
* @param s
*/
public PQueueUnsortedList(Comparator c, Sequence s) {
L = new ListDLNode();
C = c;
if (null != s) {
while (!s.isEmpty()) {
Entry e = (Entry) s.removeFirst();
insert(e.getKey(), e.getValue());
}
}
}
/**
* 统计优先队列的规模
*
* @return
*/
@Override
public int getSize() {
return L.getSize();
}
/**
* 判断优先队列是否为空
*
* @return
*/
@Override
public boolean isEmpty() {
return L.isEmpty();
}
/**
* 若Q非空,则返回其中的最小条目(并不删除);否则,报错
*
* @return
* @throws ExceptionPQueueEmpty
*/
@Override
public Entry getMin() throws ExceptionPQueueEmpty {
if (L.isEmpty()) {
throw new ExceptionPQueueEmpty("意外:优先队列空");
}
Position minPos = L.first();
Position curPos = L.getNext(minPos);
//依次检查所有位置,找出最小条目
while (null != curPos) {
if (0 < C.compare(minPos.getElem(), curPos.getElem())) {
minPos = curPos;
}
}
return (Entry) minPos.getElem();
}
/**
* 将对象obj与关键码k合成一个条目,将其插入Q中,并返回该条目
*
* @param key
* @param obj
* @return
* @throws ExceptionKeyInvalid
*/
@Override
public Entry insert(Object key, Object obj) throws ExceptionKeyInvalid {
//创建一个新条目
Entry entry = new EntryDefault(key, obj);
//接至列表末尾
L.insertLast(entry);
return (entry);
}
/**
* 若Q非空,则从其中摘除关键码最小的条目,并返回该条目;否则,报错
*
* @return
* @throws ExceptionPQueueEmpty
*/
@Override
public Entry delMin() throws ExceptionPQueueEmpty {
if (L.isEmpty()) {
throw new ExceptionPQueueEmpty("意外:优先队列空");
}
Position minPos = L.first();
Iterator it = L.positions();
//依次检查所有位置,找出最小条目
while (it.hasNext()) {
Position curPos = (Position) (it.getNext());
// System.out.println("\t" + ((Entry)(curPos.getElem())).getKey());
if (0 < C.compare(((Entry) (minPos.getElem())).getKey(), ((Entry) (curPos.getElem())).getKey())) {
minPos = curPos;
}
}
return (Entry) L.remove(minPos);
}
}
复制代码
基于有序列表的实现及分析
/**
* <b>Description:</b> 基于有序列表的实现及分析 <br>
*/
public class PQueueSortedList implements PQueue {
private List L;
private Comparator C;
/**
* 构造方法(使用默认比较器)
*/
public PQueueSortedList() {
this(new ComparatorDefault(), null);
}
/**
* 构造方法(使用指定比较器)
*
* @param c
*/
public PQueueSortedList(Comparator c) {
this(c, null);
}
/**
* 构造方法(使用指定初始元素)
*
* @param s
*/
public PQueueSortedList(Sequence s) {
this(new ComparatorDefault(), s);
}
/**
* 构造方法(使用指定比较器和初始元素)
*
* @param c
* @param s
*/
public PQueueSortedList(Comparator c, Sequence s) {
L = new ListDLNode();
C = c;
if (null != s)
while (!s.isEmpty()) {
Entry e = (Entry) s.removeFirst();
insert(e.getKey(), e.getValue());
}
}
/**
* 统计优先队列的规模
*
* @return
*/
@Override
public int getSize() {
return L.getSize();
}
/**
* 判断优先队列是否为空
*
* @return
*/
@Override
public boolean isEmpty() {
return L.isEmpty();
}
/**
* 若Q非空,则返回其中的最小条目(并不删除);否则,报错
*
* @return
* @throws ExceptionPQueueEmpty
*/
@Override
public Entry getMin() throws ExceptionPQueueEmpty {
if (L.isEmpty()) {
throw new ExceptionPQueueEmpty("意外:优先队列空");
}
return (Entry) L.last();
}
/**
* 将对象obj与关键码k合成一个条目,将其插入Q中,并返回该条目
*
* @param key
* @param obj
* @return
* @throws ExceptionKeyInvalid
*/
@Override
public Entry insert(Object key, Object obj) throws ExceptionKeyInvalid {
//创建一个新条目
Entry entry = new EntryDefault(key, obj);
//若优先队列为空 //或新条目是当前最大者
if (L.isEmpty() || (0 > C.compare(((Entry) (L.first().getElem())).getKey(), entry.getKey()))) {
//则直接插入至表头
L.insertFirst(entry);
} else {//否则
//从尾条目开始
Position curPos = L.last();
while (0 > C.compare(((Entry) (curPos.getElem())).getKey(), entry.getKey())) {
//不断前移,直到第一个不小于entry的条目
curPos = L.getPrev(curPos);
}
//紧接该条目之后插入entry
L.insertAfter(curPos, entry);
}
return (entry);
}
/**
* 若Q非空,则从其中摘除关键码最小的条目,并返回该条目;否则,报错
*
* @return
* @throws ExceptionPQueueEmpty
*/
@Override
public Entry delMin() throws ExceptionPQueueEmpty {
if (L.isEmpty()) {
throw new ExceptionPQueueEmpty("意外:优先队列空");
}
return (Entry) L.remove(L.last());
}
}
复制代码
选择排序与插入排序
选择排序
插入排序
效率比较
用堆实现优先队列
堆的定义及性质
堆结构
完全性
用堆实现优先队列
/**
* <b>Description:</b> 基于堆的优先队列及其实现 <br>
*/
public class PQueueHeap implements PQueue {
/**
* 完全二叉树形式的堆
*/
private ComplBinTree H;
/**
* 比较器
*/
private Comparator comp;
/**
* 构造方法
*/
public PQueueHeap() {
this(new ComparatorDefault(), null);
}
/**
* 构造方法:默认的空优先队列
*
* @param c
*/
public PQueueHeap(Comparator c) {
this(c, null);
}
/**
* 构造方法:根据某一序列直接批量式构造堆算法,S中元素都是形如(key, value)的条目
*
* @param S
*/
public PQueueHeap(Sequence S) {
this(new ComparatorDefault(), S);
}
/**
* 构造方法:根据某一序列直接批量式构造堆算法,s中元素都是形如(key, value)的条目
*
* @param c
* @param s
*/
public PQueueHeap(Comparator c, Sequence s) {
comp = c;
H = new ComplBinTreeVector(s);
if (!H.isEmpty()) {
//自底而上
for (int i = H.getSize() / 2 - 1; i >= 0; i--) {
//逐节点进行下滤
percolateDown(H.posOfNode(i));
}
}
}
/*-------- PQueue接口中定义的方法 --------*/
/**
* 统计优先队列的规模
*
* @return
*/
@Override
public int getSize() {
return H.getSize();
}
/**
* 判断优先队列是否为空
*
* @return
*/
@Override
public boolean isEmpty() {
return H.isEmpty();
}
/**
* 若Q非空,则返回其中的最小条目(并不删除);否则,报错
*
* @return
* @throws ExceptionPQueueEmpty
*/
@Override
public Entry getMin() throws ExceptionPQueueEmpty {
if (isEmpty()) {
throw new ExceptionPQueueEmpty("意外:优先队列为空");
}
return (Entry) H.getRoot().getElem();
}
/**
* 将对象obj与关键码k合成一个条目,将其插入Q中,并返回该条目
*
* @param key
* @param obj
* @return
* @throws ExceptionKeyInvalid
*/
@Override
public Entry insert(Object key, Object obj) throws ExceptionKeyInvalid {
checkKey(key);
Entry entry = new EntryDefault(key, obj);
percolateUp(H.addLast(entry));
return entry;
}
/**
* 若Q非空,则从其中摘除关键码最小的条目,并返回该条目;否则,报错
*
* @return
* @throws ExceptionPQueueEmpty
*/
@Override
public Entry delMin() throws ExceptionPQueueEmpty {
if (isEmpty()) {
throw new ExceptionPQueueEmpty("意外:优先队列为空");
}
//保留堆顶
Entry min = (Entry) H.getRoot().getElem();
//若只剩下最后一个条目
if (1 == getSize()) {
//直接摘除之
H.delLast();
} else {//否则
H.getRoot().setElem(((ComplBinTreeNodeRank) H.delLast()).getElem());
//取出最后一个条目,植入堆顶
percolateDown(H.getRoot());
}
//返回原堆顶
return min;
}
/*-------- 辅助方法 --------*/
/**
* 检查关键码的可比较性
*
* @param key
* @throws ExceptionKeyInvalid
*/
protected void checkKey(Object key) throws ExceptionKeyInvalid {
try {
comp.compare(key, key);
} catch (Exception e) {
throw new ExceptionKeyInvalid("无法比较关键码");
}
}
/**
* 返回节点v(中所存条目)的关键码
*
* @param v
* @return
*/
protected Object key(BinTreePosition v) {
return ((Entry) (v.getElem())).getKey();
}
/*-------- 算法方法 --------*/
/**
* 交换父子节点(中所存放的内容)
*
* @param u
* @param v
*/
protected void swapParentChild(BinTreePosition u, BinTreePosition v) {
Object temp = u.getElem();
u.setElem(v.getElem());
v.setElem(temp);
}
/**
* 上滤算法
*
* @param v
*/
protected void percolateUp(BinTreePosition v) {
//记录根节点
BinTreePosition root = H.getRoot();
//不断地
while (v != H.getRoot()) {
//取当前节点的父亲
BinTreePosition p = v.getParent();
if (0 >= comp.compare(key(p), key(v))) {
//除非父亲比孩子小
break;
}
//否则,交换父子次序
swapParentChild(p, v);
//继续考察新的父节点(即原先的孩子)
v = p;
}
}
/**
* 下滤算法
*
* @param v
*/
protected void percolateDown(BinTreePosition v) {
//直到v成为叶子
while (v.hasLChild()) {
//首先假设左孩子的(关键码)更小
BinTreePosition smallerChild = v.getLChild();
if (v.hasRChild() && 0 < comp.compare(key(v.getLChild()), key(v.getRChild()))) {
//若右孩子存在且更小,则将右孩子作为进一步比较的对象
smallerChild = v.getRChild();
}
if (0 <= comp.compare(key(smallerChild), key(v))) {
//若两个孩子都不比v更小,则下滤完成
break;
}
//否则,将其与更小的孩子交换
swapParentChild(v, smallerChild);
//并继续考察这个孩子
v = smallerChild;
}
}
}
复制代码
基于堆的优先队列及其实现
堆排序
直接堆排序
就地堆排序
Huffman 树
冒泡排序
/**
* <b>Description:</b> 起泡排序算法 <br>
*/
public class SorterBubblesort implements Sorter {
private Comparator C;
public SorterBubblesort() {
this(new ComparatorDefault());
}
public SorterBubblesort(Comparator comp) {
C = comp;
}
@Override
public void sort(Sequence S) {
int n = S.getSize();
for (int i = 0; i < n; i++) {
for (int j = 0; j < n - i - 1; j++) {
if (0 < C.compare(S.getAtRank(j), S.getAtRank(j + 1))) {
Object temp = S.getAtRank(j);
S.replaceAtRank(j, S.getAtRank(j + 1));
S.replaceAtRank(j + 1, temp);
}
}
}
}
}
复制代码