版权声明:转载请随意! https://blog.csdn.net/qq_41723615/article/details/89061022
使用链表来实现队列
下面改进链表使链表带有尾指针。
为最后一个节点添加一个指针tail做标记。
此时在head和tail两端添加节点是非常容易的。
由于链表可能不是对称的,所以在tail删除节点可能不太容易。要删除tail位置的节点,需要找到tail之前位置的节点。
怎么找到呢?还是需要从head头节点位置开始遍历寻找,所以时间复杂度还是O(n)的。
由于tail端删除元素不容易,所以要实现链表队列的话,需要以head端作为队尾删除元素,从tail端作为队首添加元素。
由于操作中不涉及中间元素的插入,所以不做虚拟头结点设置。
Queue接口:
public interface Queue<E> {
int getSize();
boolean isEmpty();
void enqueue(E e);
E dequeue();
E getFront();
}
链表队列:
public class LinkedListQueue<E> implements Queue<E> {
private class Node{
public E e;
public Node next;
public Node(E e, Node next){
this.e = e;
this.next = next;
}
public Node(E e){
this(e, null);
}
public Node(){
this(null, null);
}
@Override
public String toString(){
return e.toString();
}
}
private Node head, tail;
private int size;
public LinkedListQueue(){
head = null;
tail = null;
size = 0;
}
@Override
public int getSize(){
return size;
}
@Override
public boolean isEmpty(){
return size == 0;
}
//入队操作
@Override
public void enqueue(E e){
if(tail == null){
//尾部插一个元素
tail = new Node(e);
head = tail;
}
else{
//tail.next插入一个元素
tail.next = new Node(e);
tail = tail.next;
}
size ++;
}
//出队操作
@Override
public E dequeue(){
if(isEmpty()) {
throw new IllegalArgumentException("Cannot dequeue from an empty queue.");
}
//出队元素
Node retNode = head;
head = head.next;
retNode.next = null;
if(head == null) {
tail = null;
}
size --;
return retNode.e;
}
//查看队首元素
@Override
public E getFront(){
if(isEmpty()) {
throw new IllegalArgumentException("Queue is empty.");
}
return head.e;
}
@Override
public String toString(){
StringBuilder res = new StringBuilder();
res.append("Queue: front ");
Node cur = head;
while(cur != null) {
res.append(cur + "->");
cur = cur.next;
}
res.append("NULL tail");
return res.toString();
}
public static void main(String[] args){
LinkedListQueue<Integer> queue = new LinkedListQueue<>();
for(int i = 0 ; i < 10 ; i ++){
queue.enqueue(i);
System.out.println(queue);
if(i % 3 == 2){
queue.dequeue();
System.out.println(queue);
}
}
}
}
运行效果:
下面对数组循环队列及链表队列进行性能比较:
import java.util.Random;
public class Main {
// 测试使用q运行opCount个enqueueu和dequeue操作所需要的时间,单位:秒
private static double testQueue(Queue<Integer> q, int opCount){
long startTime = System.nanoTime();
Random random = new Random();
for(int i = 0 ; i < opCount ; i ++)
q.enqueue(random.nextInt(Integer.MAX_VALUE));
for(int i = 0 ; i < opCount ; i ++)
q.dequeue();
long endTime = System.nanoTime();
return (endTime - startTime) / 1000000000.0;
}
public static void main(String[] args) {
int opCount = 100000;
LoopQueue<Integer> loopQueue = new LoopQueue<>();
double time1 = testQueue(loopQueue, opCount);
System.out.println("LoopQueue, time: " + time1 + " s");
LinkedListQueue<Integer> linkedListQueue = new LinkedListQueue<>();
double time2 = testQueue(linkedListQueue, opCount);
System.out.println("LinkedListQueue, time: " + time1 + " s");
}
}
可以看出两者性能差距不大。