3.数据结构--队列
Posted zouke1220
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了3.数据结构--队列相关的知识,希望对你有一定的参考价值。
队列是一种先进先出的线性数据结构
1.队列的实现
public interface Queue<E> { int getSize(); boolean isEmpty(); void enqueue(E e); E dequeue(); E getFront(); }
实现队列
public class ArrayQueue<E> implements Queue<E>{ private Array<E> array; public ArrayQueue(int capacity){ array = new Array<>(capacity); } public ArrayQueue(){ array = new Array<>(); } @Override public int getSize() { return array.getSize(); } @Override public boolean isEmpty(){ return array.isEmpty(); } public int getCapacity(){ return array.getCapacity(); } @Override public void enqueue(E e) { array.addLast(e); } @Override public E dequeue() { return array.removeFirst(); } @Override public E getFront(){ return array.getFirst(); } @Override public String toString(){ StringBuilder res = new StringBuilder(); res.append("Queue:"); res.append("front ["); for(int i = 0;i < array.getSize();i ++){ res.append(array.get(i)); if(i != array.getSize() - 1) res.append(","); } res.append("] tail"); return res.toString(); } }
调用实例
public class Main { public static void main(String[] args) { ArrayQueue<Integer> queue = new ArrayQueue<>(); for(int i = 0;i < 10;i ++){ queue.enqueue(i); System.out.println(queue); if(i % 3 == 2){ queue.dequeue(); System.out.println(queue); } } // Queue:front [0] tail // Queue:front [0,1] tail // Queue:front [0,1,2] tail // Queue:front [1,2] tail // Queue:front [1,2,3] tail // Queue:front [1,2,3,4] tail // Queue:front [1,2,3,4,5] tail // Queue:front [2,3,4,5] tail // Queue:front [2,3,4,5,6] tail // Queue:front [2,3,4,5,6,7] tail // Queue:front [2,3,4,5,6,7,8] tail // Queue:front [3,4,5,6,7,8] tail // Queue:front [3,4,5,6,7,8,9] tail } }
2.数组队列的复杂度分析
3.数组队列的问题
循环队列
tail和front互相追赶着,这个追赶过程就是队列添加和删除的过程,如果tail追到front说明队列满了,如果front追到tail说明队列为空。
令队列空间中的一个单元闲置,使得队列非空时,tail与front之间至少间隔一个空闲单元。
当tail=front的时候,队列可能是满,也可能是空。
因为存在满和空两种情况,我们需要分别判断:
☆满:当队列添加元素到tail的下一个元素是front的时候,也就是转圈子要碰头了,我们就认为队列满了。(tail+1)%c=front
☆空:当队列删除元素到front=tail的时候,我们认为队列空了。tail==front,不一定为0
(1) 入队
(2)出队
public class LoopQueue<E> implements Queue<E> { private E[] data; private int front,tail; private int size; public LoopQueue(int capacity){ data = (E[])new Object[capacity + 1]; front = 0; tail = 0; size =0; } public LoopQueue(){ this(10); } //获取队列容量 public int getCapacity(){ return data.length - 1; } //判断队列是否为空 @Override public boolean isEmpty(){ return front == tail; } //获取队列元素个数 @Override public int getSize(){ return size; } //入队 @Override public void enqueue(E e){ //队列满 if((tail + 1) % data.length == front) resize(getCapacity() * 2); //扩容 //将入队元素放到尾指针指向的位置 data[tail] = e; tail =(tail +1) % data.length; //由于是循环队列,所以后面要% data.length /* * front tail * 0 1 2 3 4 5 6 7 8 9 //3、4、5、6是满的 * 接着入队的元素,要放到(6+1) % 10=7 指针所指的位置处 * front tail * 0 1 2 3 4 5 6 7 8 9 //3、4、5、6、7、8、9是满的 * 最右侧已经满了,后面入队元素要放到(9+1) % 10=0 指针所指的位置处 * * tail front * 0 1 2 3 4 5 6 7 8 9 //3、4、5、6、7、8、9、0是满的 * 下一个入队元素,放的位置就是(0+1) % 10=1 指针所指的位置处 */ size ++; } //出队 @Override public E dequeue(){ if(isEmpty()) throw new IllegalArgumentException("cannot dequeue from an empty queue"); E ret = data[front]; //保存出队元素 data[front] = null; //出队 front = (front + 1) % data.length; //由于是循环队列,所以后面要% data.length size --; if(size == getCapacity() / 4 && getCapacity() / 2 != 0) resize(getCapacity() / 2); //缩容 return ret; } @Override public E getFront(){ if(isEmpty()) throw new IllegalArgumentException("Queue is Empty"); return data[front]; } private void resize(int newCapacity){ E[] newData = (E[])new Object[newCapacity + 1]; //此处由于原来浪费了1个空间,所以扩容时要加上这1个 for(int i = 0;i < size;i ++) //把原来的位置的元素重新放到新开辟的空间内,从指针位置0处开始放 newData[i] = data[(i + front) % data.length]; //由于是循环队列,所以后面要% data.length /* * tail front * - - - - - - - - - * 0 1 2 3 4 5 6 7 8 9 * 上面的队列已满,开始扩容 * * 扩容后为原来的2倍,原来位置的数据要重新放到新的空间,从指针0处开始放置 * front tail * - - - - - - - - - * 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 * * 原来位置 新位置 * (0 + 2) % 10 = 2 0 * (1 + 2) % 10 = 3 1 * (2 + 2) % 10 = 4 4 * ............... */ data = newData; front = 0; tail = size; } @Override public String toString(){ StringBuilder res = new StringBuilder(); res.append(String.format("Queue:size = %d , capacity = %d ",size,getCapacity())); res.append("front ["); //非循环队列 //for(int i = 0; i < size ; i++) //循环队列 /* * 第一次循环 front=2 i=(2 + 1) % 10 = 3 * 第二次循环 front=2 i=(3 + 1) % 10 = 4 * .... * 第八次循环 front=2 i=(9 + 1) % 10 = 0 * 第九次循环 front=2 i=(0 + 1) % 10 = 1 */ for(int i = front;i != tail;i = (i + 1) % data.length){ res.append(data[i]); if((i + 1) % data.length != tail) res.append(", "); } res.append("] tail"); return res.toString(); } }
调用循环队列
public class Main { public static void main(String[] args) { LoopQueue<Integer> queue = new LoopQueue<>(); for(int i = 0;i < 10;i ++){ queue.enqueue(i); System.out.println(queue); if(i % 3 == 2){ queue.dequeue(); System.out.println(queue); } } // Queue:size = 1 , capacity = 10 // front [0] tail // Queue:size = 2 , capacity = 10 // front [0, 1] tail // Queue:size = 3 , capacity = 10 // front [0, 1, 2] tail // Queue:size = 2 , capacity = 5 // front [1, 2] tail // Queue:size = 3 , capacity = 5 // front [1, 2, 3] tail // Queue:size = 4 , capacity = 5 // front [1, 2, 3, 4] tail // Queue:size = 5 , capacity = 5 // front [1, 2, 3, 4, 5] tail // Queue:size = 4 , capacity = 5 // front [2, 3, 4, 5] tail // Queue:size = 5 , capacity = 5 // front [2, 3, 4, 5, 6] tail // Queue:size = 6 , capacity = 10 // front [2, 3, 4, 5, 6, 7] tail // Queue:size = 7 , capacity = 10 // front [2, 3, 4, 5, 6, 7, 8] tail // Queue:size = 6 , capacity = 10 // front [3, 4, 5, 6, 7, 8] tail // Queue:size = 7 , capacity = 10 // front [3, 4, 5, 6, 7, 8, 9] tail } }
4.循环队列的复杂度分析
5.数组队列和循环队列的比较
import java.util.Random; public class Main { //测试使用q运行opCount个enqueue和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; //数组队列 ArrayQueue<Integer> arrayQueue = new ArrayQueue<>(); double time1 = testQueue(arrayQueue,opCount); System.out.println("ArrayQueue, time:" + time1 + "s"); //ArrayQueue, time:4.859302024s //循环队列 LoopQueue<Integer> loopQueue = new LoopQueue<>(); double time2 = testQueue(loopQueue,opCount); System.out.println("LoopQueue, time:" + time2 + "s"); //LoopQueue, time:0.019878675s } }
以上是关于3.数据结构--队列的主要内容,如果未能解决你的问题,请参考以下文章