队列、双端队列与优先队列
队列:先进先出,从后面插入,从前面移除;处理类似排队的问题,先排先处理,插入和移除操作的时间复杂度都为O(1)。
双端队列:即在队列两端都可以插入和删除。同时拥有栈和队列的功能,一般使用频率较低,时间复杂度 O(1)。
优先级队列:内部维护一个按优先级排序的序列。插入时需要比较查找插入的位置,时间复杂度O(N), 删除O(1)。
1、基于链表的队列实现
元素从链尾插入,从链头删除。队列的链表实现含有两个数据域:firstNode——引用链表的第一个结点,用于快速删除或访问队列的首数据;lastNode——引用链表的最后一个结点,用于向队列快速插入新元素。
队列接口
public interface QueueInterface<T> {
public void enqueue(T newEntry);
public T dequeue();
public T getFront();
public boolean isEmpty();
public void clear();
}
链表队列实现
public class LinkedQueue<T> implements QueueInterface<T>{
private Node firstNode;
private Node lastNode;
public LinkedQueue() {
firstNode=null;
lastNode=null;
}
@Override
public void enqueue(T newEntry) {
System.out.print(newEntry+" ");
Node newNode=new Node(newEntry);
if(isEmpty())
firstNode=newNode;
else
lastNode.setNextNode(newNode);
lastNode=newNode;
}
@Override
public T dequeue() {
T front=null;
if(!isEmpty()){
front=firstNode.getEntry();
firstNode=firstNode.getNextNode();
if(firstNode==null)
lastNode=null;
}
return front;
}
@Override
public T getFront() {
T front=null;
if(!isEmpty())
front=firstNode.getEntry();
return front;
}
@Override
public boolean isEmpty() {
return firstNode==null;
}
@Override
public void clear() {
firstNode=null;
lastNode=null;
}
public void display(){
Node front=firstNode;
System.out.println();
while(front!=null){
System.out.print(front.getEntry()+" ");
front=front.getNextNode();
}
System.out.println();
}
private class Node{
private T entry;
private Node nextNode;
public Node(T newEntry) {
entry=newEntry;
nextNode=null;
}
public T getEntry() {
return entry;
}
public void setEntry(T entry) {
this.entry = entry;
}
public Node getNextNode() {
return nextNode;
}
public void setNextNode(Node nextNode) {
this.nextNode = nextNode;
}
}
}
测试代码
public class main_LinkedQueue {
private static LinkedQueue<Integer> queue;
public static void main(String[] args) {
queue=new LinkedQueue<Integer>();
Random ra =new Random();
for(int i=0;i<20;i++)
queue.enqueue(ra.nextInt(100));
queue.display();
}
}
基于链表的队列实现的各种操作(插入、删除、检索链头)的时间复杂度均为O(1)。
2、基于数组的队列实现
使用数组实现队列,用frontIndex和backIndex分别表示队列前端与后端的索引。如果检出把首个元素放在queue[0]处,那么删除时其余数据需要依次向前移动,这样效率太低了。
这里将采用
循环数组,删除前端时,其它元素仍保留在原位置上,frontIndex表示下一个元素的索引;当插入元素时,一旦到达数组末尾,就可以将后续的元素插入到数组的头部。如下图(a)删除队列的3个元素,得到图(b),然后插入5个元素得到图(c)。
如何检测何时数组已满?图(c)插入两个元素后得到图(d),此时数组已满;图(b)删除3个元素后得到图(e),此时数组是空的。可以看出,队列空与队列满时frontIndex=backIndex+1,因此无法以此判断数组是否满。
若留出一个数组位置未用,则可以只通过考察frontIndex和backIndex就能区分空队列和满队列。
队列接口
public interface QueueInterface<T> {
public void enqueue(T newEntry);
public T dequeue();
public T getFront();
public boolean isEmpty();
public void clear();
}
数组队列实现
public class ArrayQueue<T> implements QueueInterface<T>{
private T[] queue;
private int frontIndex;
private int backIndex;
private static final int DEFAULT_INITIAL_CAPACITY=20;
public ArrayQueue() {
this(DEFAULT_INITIAL_CAPACITY);
}
public ArrayQueue(int initialCapacity) {
queue=(T[]) new Object[initialCapacity+1];
frontIndex=0;
backIndex=initialCapacity;
}
@Override
public void enqueue(T newEntry) {
if(isArrayFull())
doubleArray();
backIndex=(backIndex+1)%queue.length;
queue[backIndex]=newEntry;
}
@Override
public T dequeue() {
T front=null;
if(!isEmpty()){
front=queue[frontIndex];
queue[frontIndex]=null;
frontIndex=(frontIndex+1)%queue.length;
}
return front;
}
@Override
public T getFront() {
T front=null;
if(!isEmpty())
front=queue[frontIndex];
return front;
}
@Override
public boolean isEmpty() {
return frontIndex==(backIndex+1)%queue.length;
}
@Override
public void clear() {
while(!isEmpty()){
dequeue();
}
}
public void display(){
System.out.println();
int index=frontIndex;
while(index!=backIndex+1){
System.out.print(queue[index]+" ");
index=(index+1)%queue.length;
}
}
private boolean isArrayFull(){
return frontIndex==(backIndex+2)%queue.length;
}
private void doubleArray(){
T[] oldQueue=queue;
int oldSize=oldQueue.length;
queue=(T[]) new Object[2*oldSize];
for(int index=0;index<oldSize-1;index++){
queue[index]=oldQueue[frontIndex];
frontIndex=(frontIndex+1)%oldSize;
}
frontIndex=0;
backIndex=oldSize-2;
}
}
测试代码
public class main_ArrayQueue {
private static ArrayQueue<Integer> queue;
public static void main(String[] args) {
queue=new ArrayQueue<Integer>();
Random ra =new Random();
for(int i=0;i<15;i++)
queue.enqueue(ra.nextInt(100));
queue.display();
for(int i=0;i<5;i++)
queue.dequeue();
for(int i=0;i<12;i++)
queue.enqueue(ra.nextInt(100));
queue.display();
}
}
3、基于双端链表的双端队列实现
队列的每一个结点既能引用下一个结点,又能引用上一个结点。
队列接口
public interface QueueInterface<T> {
public void addToBack(T newEntry);
public void addToFront(T newEntry);
public T removeBack();
public T removeFront();
public T getBack();
public T getFront();
public boolean isEmpty();
public void clear();
}
双端队列实现
public class Doubly_LinkedQueue<T> implements QueueInterface<T>{
private Node firstNode;
private Node lastNode;
public Doubly_LinkedQueue() {
firstNode=null;
lastNode=null;
}
@Override
public void addToBack(T newEntry) {
System.out.print(newEntry+" ");
Node newNode=new Node(newEntry, lastNode, null);
if(isEmpty())
firstNode=newNode;
else
lastNode.setNextNode(newNode);
lastNode=newNode;
}
@Override
public void addToFront(T newEntry) {
System.out.print(newEntry+" ");
Node newNode=new Node(newEntry, null, firstNode);
if(isEmpty())
lastNode=newNode;
else
firstNode.setPreviousNode(newNode);
firstNode=newNode;
}
@Override
public T removeBack() {
T back=null;
if(!isEmpty()){
back=lastNode.getData();
lastNode=lastNode.getPreviousNode();
if(lastNode==null)
firstNode=null;
else
lastNode.setNextNode(null);
}
return back;
}
@Override
public T removeFront() {
T front=null;
if(!isEmpty()){
front=firstNode.getData();
firstNode=firstNode.getNextNode();
if(firstNode==null)
lastNode=null;
else
firstNode.setPreviousNode(null);
}
return front;
}
@Override
public T getBack() {
T back=null;
if(isEmpty())
back=lastNode.getData();
return back;
}
@Override
public T getFront() {
T front=null;
if(isEmpty())
front=firstNode.getData();
return front;
}
@Override
public boolean isEmpty() {
return firstNode==null && lastNode==null;
}
@Override
public void clear() {
while(!isEmpty())
removeFront();
}
public void displayF_to_B(){
Node currentNode=firstNode;
System.out.println();
while(currentNode!=null){
System.out.print(currentNode.getData()+" ");
currentNode=currentNode.getNextNode();
}
}
public void displayB_to_F(){
Node currentNode=lastNode;
System.out.println();
while(currentNode!=null){
System.out.print(currentNode.getData()+" ");
currentNode=currentNode.getPreviousNode();
}
}
private class Node{
private T Data;
private Node previousNode;
private Node nextNode;
public Node(T newEntry){
this(newEntry,null,null);
}
public Node(T newEntry,Node previousNode,Node nextNode) {
this.Data=newEntry;
this.previousNode=previousNode;
this.nextNode=nextNode;
}
public T getData() {
return Data;
}
public void setData(T data) {
Data = data;
}
public Node getPreviousNode() {
return previousNode;
}
public void setPreviousNode(Node previousNode) {
this.previousNode = previousNode;
}
public Node getNextNode() {
return nextNode;
}
public void setNextNode(Node nextNode) {
this.nextNode = nextNode;
}
}
}