您的位置:首页 > 其它

利用双向队列构造链接式优先队列

2015-10-24 12:21 288 查看
优先队列的定义

优先队列中的元素被赋予优先级。插入元素时保持优先队列部分有序,删除元素时具有最高优先级的元素最先删除。优先队列分为面向最高优先级的优先队列和面向最低优先级的优先队列。

数据结构二叉堆

当一棵二叉树的每个结点都大于等于它的两个子结点时,这个二叉树称为堆有序,也被称作二叉堆。一般二叉堆都是一棵完全二叉树,如图所示:



基于二叉堆的优先队列

二叉堆可以很好的实现优先队列的基本操作:插入元素和删除优先级最高或最低的元素。二叉堆可以直接用数组实现,这里只讨论链接形式。首先需要保证二叉堆是一个完全二叉树,一棵完全二叉树可以通过队列来构造,可以参考我的另一篇博客利用队列构造链接式完全二叉树

而基于二叉堆的优先队列有插入和删除操作,因而要使用双向队列deque来实现:

新节点插入上层节点(deque队头)的左孩子或右孩子,同时用addLast将新节点加入deque队尾,上层节点的左右孩子插满后用removeFirst删除deque队头的上层节点,这里的规律是deque队尾始终是完全二叉树的末尾节点,而其队头始终是下一次插入节点的父节点

删除节点则相反,用removeLast删除deque队尾节点(完全二叉树的末尾节点),同时用addFirst将该节点的父节点插入deque队头(下一次插入节点的父节点),这么做仍然保持了上述规律。

优先队列中还有非常重要的两个操作:插入节点上游(swim)和删除节点下沉(sink),这两个操作可以保持优先队列二叉堆部分有序的特性。

链接式优先队列的实现(面向最大元素)

public class ListMaxPQ<Key extends Comparable<Key>> {
private class Node {
Key item;
Node parent;
Node lchild, rchild;
}
private Node root;// 二叉堆根节点
private Deque<Node> deque = new Deque<>();// 双向队列
private int currentLength;

public ListMaxPQ() {
root = new Node();
deque.addLast(root);// 首先让根节点入队
currentLength = 0;
}

public void insert(Key v) {
if (isEmpty()) {
root.item = v;
currentLength++;
return;
}
Node newNode = new Node();// 新建节点
newNode.item = v;
while (!deque.isEmpty()) {
Node t = deque.peek();// 取队头节点但不出队
if (t.lchild == null) {
t.lchild = newNode;// 建立双向链接
newNode.parent = t;
deque.addLast(t.lchild);// 左孩子插入deque队尾
currentLength++;
swim(newNode);// 新节点上游
return;// 仅插入左孩子的节点不出队,之后插入其右孩子
}
if (t.rchild == null) {
t.rchild = newNode;// 建立双向链接
newNode.parent = t;
deque.addLast(t.rchild);// 右孩子插入deque队尾
deque.removeFirst();// 插入右孩子的节点处理完毕,让其出队
currentLength++;
swim(newNode);// 新节点上游
return;
}
}
}

public Key delMax() {
if (currentLength==0) {
return null;
}else if (currentLength == 1) {
currentLength--;
return root.item;
}
Key max = root.item;// 根节点即将删除的最大节点
Node last = deque.removeLast();// 删除deque队尾(完全二叉树的末尾节点)
if (last.parent.rchild == last) {// 如果是右孩子则将父节点恢复到deque中
deque.addFirst(last.parent);
}
exch(last, root);// 交换末尾节点和根节点
if (last.parent.lchild == last) {// 以下操作是删除末尾节点的任何链接,作为垃圾被回收
last.parent.lchild = null;
} else {
last.parent.rchild = null;
}
last.parent = null;
last = null;
currentLength--;
sink(root);// 根节点下沉
return max;
}

private void swim(Node node) {
while (node.parent != null && less(node.parent, node)) {
exch(node.parent, node);
node = node.parent;
}
}

private void sink(Node node) {
while (node.lchild != null) {
Node t = node.lchild;
if (node.rchild != null && less(t, node.rchild)) {
t = node.rchild;
}
if (!less(node, t)) {
break;
}
exch(node, t);
node = t;
}
}

private boolean less(Node v, Node w) {
return v.item.compareTo(w.item) < 0;
}

private void exch(Node a, Node b) {
Key t = a.item;
a.item = b.item;
b.item = t;
}

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

public int size() {
return currentLength;
}

public void show() {// 按照广度优先的顺序遍历
ListQueue<Node> itQueue = new ListQueue<>();// 层次信息的队列
itQueue.enqueue(root);
while (!itQueue.isEmpty()) {
Node t = itQueue.dequeue();// 根节点出队
System.out.print(t.item + " ");
// 分别将左右孩子入队,成为下一层节点
if (t.lchild != null) {
itQueue.enqueue(t.lchild);
}
if (t.rchild != null) {
itQueue.enqueue(t.rchild);
}
}
System.out.println();
}

public static void main(String[] args) {
ListMaxPQ<Integer> pq = new ListMaxPQ<>();
for (int i = 0; i < 20; i++) {
pq.insert(i);
}
pq.show();
while (!pq.isEmpty()) {
System.out.print(pq.delMax() + " ");
}
}
}


最后展示的是一个简单的双向队列deque的实现:

public class Deque<T> {
private class DeNode{
T item;
DeNode front;
DeNode next;
}
private DeNode first;
private DeNode last;
private int currentLength;

public void addFirst(T item) {
DeNode newNode=new DeNode();
newNode.item=item;
newNode.next=first;
if (isEmpty()) {
first=newNode;
last=first;
}else {
first.front=newNode;
first=newNode;
}
}

public void addLast(T item) {
DeNode newNode=new DeNode();
newNode.item=item;
newNode.front=last;
if (isEmpty()) {
last=newNode;
first=last;
}else {
last.next=newNode;
last=newNode;
}
}

public T removeFirst() {
if (isEmpty()) {
throw new NullPointerException();
}
T item=first.item;
first.next.front=null;
first=first.next;
return item;
}

public T removeLast() {
if (isEmpty()) {
throw new NullPointerException();
}
T item=last.item;
last.front.next=null;
last=last.front;
return item;
}

public T peek() {
DeNode f=first;
return (f==null)?null:f.item;
}

public boolean isEmpty() {
return first==null&&last==null;
}

public int size() {
return currentLength;
}
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: