前几次我给大家分享了顺序表和栈,这次给大家分享队列。
何为队列:
由前几次的经验,我们书写一个Queue接口:定义如下
public interface Queue<E> extends Iterable<E>{
//获取队列中元素的个数
int getSize();
//判断队列是否为空
boolean isEmpty();
//入队一个元素
void enqueue(E e);
//出队一个元素
E dequeue();
//获取队头
E getFront();
//获取队尾
E getRear();
//清空队列
void clear();
}
我们发现,队列其实与线性表相似,只不过将顺序表的两端固定为队列的的头和尾。那么我们同样可以使用顺序表来实现队列:
有了前几次的经验,这次我们直接实现:
import java.util.Iterator;
public class ArrayQueue<E> implements Queue<E> {
private ArrayList<E> list;
public ArrayQueue(){
list=new ArrayList<>();
}
@Override
public int getSize() {
return list.getSize();
}
@Override
public boolean isEmpty() {
return list.isEmpty();
}
@Override
public void enqueue(E e) {
list.addLast(e);
}
@Override
public E dequeue() {
return list.removeFirst();
}
@Override
public E getFront() {
return list.getFirst();
}
@Override
public E getRear() {
return list.getLast();
}
@Override
public void clear() {
list.clear();
}
@Override
public Iterator<E> iterator() {
return list.iterator();
}
@Override
public String toString() {
StringBuilder sb=new StringBuilder();
sb.append(String.format("ArrayQueue: %d/%d\n",getSize(),list.getCapacity()));
sb.append('[');
if(isEmpty()){
sb.append(']');
}else{
for(int i=0;i<list.getSize();i++){
sb.append(list.get(i));
if(i==list.getSize()-1){
sb.append(']');
}else{
sb.append(',');
}
}
}
return sb.toString();
}
}
书写一个顺序队列的测试类:
/**
* <p>Title: TestArrayQueue.java</p>
* <p>Description: </p>
* <p>Copyright: Copyright (c) 2017</p>
* <p>Company: www.yaud.com</p>
* @author XuHaobo
* @date 2019年12月9日
* @version 1.0
*/
/**
* <p>Title: TestArrayQueue</p>
* <p>Description: </p>
* @author XuHaobo
* @date 2019年12月9日
*/
public class TestArrayQueue {
public static void main(String[] args) {
ArrayQueue<Integer> queue =new ArrayQueue<>();
for(int i=1;i<=15;i++) {
queue.enqueue(i);
}
System.out.println(queue);
for(int i=1;i<10;i++) {
queue.dequeue();
}
System.out.println(queue);
}
}
执行结果如下:
实现了顺序队列,猛然发现,顺序队列不太友好,
第一步优化:定义头队列的头和尾,使每次移动,头和尾直接指向头尾元素
第二步优化:那么我们考虑使其变为循环队,那么当队头/队尾移动到尾部的时候只需指向头部即可。
那么此时同样出现了问题:队列满和未满条件一样?我干!。
第三步优化:将一个空间预留出来,不存任何元素,尾指针直接指向这个null空间。
那么此时,队列满和判空条件不同,便解决了问题,实现了优化。
真~奥义机智。
接下来我们来书写循环队列:需要有头和尾,有效元素个数,以及一个空的空间。
//循环队列
public class ArrayQueueLoop<E> implements Queue<E> {
private E[] data;
private int front;
private int rear;
private int size;
public ArrayQueueLoop(){
data= (E[]) (new Object[11]);//因为有一个空的空间
front=0;
rear=0;
size=0;
}
获取元素个数和判空操作。
@Override
public int getSize() {
return size;
}
@Override
public boolean isEmpty() {
return size==0&&front==rear;
}
入队列和出队列操作,同样需要判断并缩容和扩容。
操作时注意转换环形和直线型,头亦是尾,最好自己画图。
@Override
public void enqueue(E e) {
if((rear+1)%data.length==front){
resize(2*data.length-1);
}
data[rear]=e;
rear=(rear+1)%data.length;
size++;
}
@Override
public E dequeue() {
if(isEmpty()){
throw new IllegalArgumentException("队列空");
}
E ret=data[front];
front=(front+1)%data.length;
size--;
if(size<=(data.length-1)/4&&(data.length-1)/2>=10){
resize((data.length-1)/2+1);
}
return ret;
}
private void resize(int newLen){
E[] newData= (E[]) (new Object[newLen]);
int p=front;
int i=0;
while(true){
newData[i]=data[p];
i++;
p=(p+1)%data.length;
if(p==rear){
break;
}
}
front=0;
rear=size;
data=newData;
}
获取队列的队头队尾元素,
@Override
public E getFront() {
if(isEmpty()){
throw new IllegalArgumentException("队列为空");
}
return data[front];
}
@Override
public E getRear() {
if(isEmpty()){
throw new IllegalArgumentException("队列为空");
}
return data[(rear-1+data.length)%data.length];
}
判空,以及字符串输出。
@Override
public void clear() {
data= (E[]) (new Object[11]);//因为有一个空的空间
front=0;
rear=0;
size=0;
}
@Override
public String toString() {
StringBuilder sb=new StringBuilder();
sb.append(String.format("ArrayQueueLoop: %d/%d\n",size,data.length-1));
sb.append('[');
if(isEmpty()){
sb.append(']');
}else{
for(int i=front;i!=rear;i=(i+1)%data.length){
sb.append(data[i]);
if((i+1)%data.length==rear){
sb.append(']');
}else{
sb.append(',');
}
}
}
return sb.toString();
}
重写迭代器,只有下一个的指向与list有所区别
@Override
public Iterator<E> iterator() {
return new ArrayQueueLoopIterator();
}
private class ArrayQueueLoopIterator implements Iterator{
int p=front;
@Override
public boolean hasNext() {
return p!=rear;
}
@Override
public Object next() {
E ret=data[p];
p=(p+1)%data.length;
return ret;
}
}
循环队列完整代码如下:
import java.util.Iterator;
//循环队列
public class ArrayQueueLoop<E> implements Queue<E> {
private E[] data;
private int front;
private int rear;
private int size;
public ArrayQueueLoop(){
data= (E[]) (new Object[11]);//因为有一个空的空间
front=0;
rear=0;
size=0;
}
@Override
public int getSize() {
return size;
}
@Override
public boolean isEmpty() {
return size==0&&front==rear;
}
@Override
public void enqueue(E e) {
if((rear+1)%data.length==front){
resize(2*data.length-1);
}
data[rear]=e;
rear=(rear+1)%data.length;
size++;
}
@Override
public E dequeue() {
if(isEmpty()){
throw new IllegalArgumentException("队列空");
}
E ret=data[front];
front=(front+1)%data.length;
size--;
if(size<=(data.length-1)/4&&(data.length-1)/2>=10){
resize((data.length-1)/2+1);
}
return ret;
}
private void resize(int newLen){
E[] newData= (E[]) (new Object[newLen]);
int p=front;
int i=0;
while(true){
newData[i]=data[p];
i++;
p=(p+1)%data.length;
if(p==rear){
break;
}
}
front=0;
rear=size;
data=newData;
}
@Override
public E getFront() {
if(isEmpty()){
throw new IllegalArgumentException("队列为空");
}
return data[front];
}
@Override
public E getRear() {
if(isEmpty()){
throw new IllegalArgumentException("队列为空");
}
return data[(rear-1+data.length)%data.length];
}
@Override
public void clear() {
data= (E[]) (new Object[11]);//因为有一个空的空间
front=0;
rear=0;
size=0;
}
@Override
public String toString() {
StringBuilder sb=new StringBuilder();
sb.append(String.format("ArrayQueueLoop: %d/%d\n",size,data.length-1));
sb.append('[');
if(isEmpty()){
sb.append(']');
}else{
for(int i=front;i!=rear;i=(i+1)%data.length){
sb.append(data[i]);
if((i+1)%data.length==rear){
sb.append(']');
}else{
sb.append(',');
}
}
}
return sb.toString();
}
@Override
public Iterator<E> iterator() {
return new ArrayQueueLoopIterator();
}
private class ArrayQueueLoopIterator implements Iterator{
int p=front;
@Override
public boolean hasNext() {
return p!=rear;
}
@Override
public Object next() {
E ret=data[p];
p=(p+1)%data.length;
return ret;
}
}
}
书写一个测试类用于测试:
/**
* <p>Title: TestArrayQueueLoop</p>
* <p>Description: </p>
* @author XuHaobo
* @date 2019年12月8日
*/
public class TestArrayQueueLoop {
public static void main(String[] args) {
ArrayQueueLoop<Integer> loop=new ArrayQueueLoop<>();
System.out.println(loop);
for(int i=1;i<=15;i++){
loop.enqueue(i);
}
System.out.println(loop);
for(int i=2;i<6;i++) {
loop.dequeue();
}
System.out.println(loop);
}
}
运行结果如下:
好了,今天的分享就到这里啦!!我们下次再见嗷。奥里给的嗷!!!!
来源:CSDN
作者:风起云☁️
链接:https://blog.csdn.net/qq_42707739/article/details/103487024