CH3 栈和队列4 - 循环队列(LoopQueue-基于动态数组)

循环队列的思想

LoopQueue循环队列的内部也是一个动态数组;
在队列中,front指向队首,tail指向队尾的下一个index,也就是下个元素入队的位置。
size代表循环队列中有效元素的个数。

循环队列的规定事项

队列为空:
front == tail 时队列为空
队列为满:
因为上面的规定,而且tail总是指向最后一个元素的下一个位置,所以为了避免与上面冲突,在这里规定LoopQueue中要浪费一个位置
(tail +1) % c = front 时,队列满

几个变量的解释

1、getCapacity() : 数组容量,在创建循环队列时,用户传入的队列大小(实际上是数组的大小,并且这个数组在使用时会动态扩容)
2、data.length : data是LoopQueue中定义的类变量,是一个数组。它的长度代表了LoopQueue的实际大小,这个对用户是隐藏的
3、size:有效元素的个数
4、tail入队enqueue时增加移动,入队时front不动
front出队时dequeueu增加移动,出队时tail不动
data.length = getCapacity() + 1

队列接口

接口Queue规定了必须实现的方法,这些
public interface Queue {
void enQueue(E e);
E deQueue();
E getFront();
boolean isEmpty();
int getSize();
}

循环队列实现的方法

一、变量
private int front; //指向队列头部
private int tail; //指向队列最后一个元素的下一个索引,也就是即将下一个被插入的位置
private int size; //队列有效元素的个数

private E[] data; //泛型数组 所以在本篇中LoopQueue的底层其实是线性动态数组

二、构造函数
构造函数
//有参构造函数,并且初始化变量
public LoopQueue(int capacity){
== data = (E[])new Object[capacity + 1]; == //注意这里,实际new出的数组比设想new出的大1
front = 0;
tail = 0;
size = 0;
}
//无参构造函数,调用this(),也就是调用了上面有参的构造函数
public LoopQueue() { this(10); }

三、基本方法
int getCapacity() data.length -1
int getSize()
boolean isEmpty() front == tail
String toString()

四、功能方法
void enQueue(E e)
E deQueue()
E getFront()
void resize(int newCapacity)

enQueue前先判断是否满了,满了就resize
再入队
然后依次改变front tail size

deQueue前先判断是否为空
不空时:出队,被出队的位置要null,防止闲逛对象
然后front移动,改变size

resize()
1 newCapacity 2 data依次放入newData 3 data指向改变 4 改变front tail

循环队列实现中的一些需要注意的点

位移变量用data.length
扩容用getCapacity()
有效数组个数用size

循环队列的循环方法
方法一:
i从0到size循环-------然后front依次加上循环i
for(i=0; i<size; i++)
data[(front + i) % data.length] 可以表示每个元素从front到tail

方法二:
i从front循环到tail(这里写成!=tail)----然后直接使用i
for(i=front; i!=tail; i = (i+1)%data.length )
data[i] 可以表示每个元素从front到tail

*循环队列计算front和tail时要循环起来,利用%
移动front和tail时也要考虑循环%

循环队列的复杂度分析

Queue
void enqueue(E) 均摊O(1)
E dequeue() 均摊O(1)
E getFront() O(1)
int getSize() O(1)
boolean isEmpty() O(1)

LoopQueue的实现
public class LoopQueue<E> implements Queue<E>{

    private int front; //指向队列头部
    private int tail; //指向队列最后一个元素的下一个索引,也就是即将下一个被插入的位置
    private int size; //队列有效元素的个数

    private E[] data; //泛型数组

    //有参构造函数,并且初始化变量
    public LoopQueue(int capacity){
        data = (E[])new Object[capacity + 1]; //注意这里,实际new出的数组比设想new出的大1
        front = 0;
        tail = 0;
        size = 0;
    }

    //无参构造函数,调用this(),也就是调用了上面有参的构造函数
    public LoopQueue() {
        this(10);
    }

    //获取循环队列中的容量(比实际队列大小少1)
    public int getCapacity() {
        return data.length -1;
    }

    //获取循环队列中的有效元素个数
    @Override
    public int getSize() {
        return size;
    }

    //判断循环队列是否为空
    @Override
    public boolean isEmpty() {
        return front == tail;
    }

    @Override
    public void enQueue(E e) {
        //首先判断队列是否为满
        //有位移循环时要用 data.length,而不是getCapacity()
        if((tail + 1) % data.length == front) {
            resize(getCapacity() * 2); //之前循环队列用户定义的大小再*2
        }
        //然后入队尾,更改tail和size
        data[tail] = e;
        tail = (tail + 1) % data.length; //tail移动需要用 %
        size ++;
    }

    @Override
    public E deQueue() {

        //首先判断队列是否为空(空不能出队)
        if (front == tail) {
            throw new IllegalArgumentException("Cannot dequeue from an empty queue.");
        }

        E frontElement;
        frontElement = data[front]; 
        data[front] = null;  //注意这里,data[front]出循环队列了,那么原本存在这里的需要释放,否则会造成loitering objects 闲逛对象
        front = (front + 1) % data.length; //front移动需要用 %
        size --;

        if (size == getCapacity() / 4 && getCapacity() / 2 != 0) { //注意下面调用resize(参数),这个参数!=0 ,要判断一下
            resize(getCapacity() / 2);
        }

        return frontElement;
    }

    @Override
    public E getFront() {
        //首先判断队列是否为空(空不能出队)
        if (front == tail) {
            throw new IllegalArgumentException("LoopQueue is empty.");
        }
        return data[front];
    }

    private void resize(int newCapacity){
        E[] newData = (E[])new Object[newCapacity + 1]; //这里要注意,再次扩容/缩容时,都要有这个+1
        for (int i = 0; i < size; i ++) {
            newData[i] = data[(front + i) % data.length];
        }
        data = newData;
        front = 0;
        tail = size;
    }

    @Override
    public String toString() {
        StringBuilder res = new StringBuilder();
        res.append("LoopQueue : front [");

        for (int i = front; i != tail; i = (i + 1) % data.length ) {
            String val = String.valueOf(data[i]);
            if ((i + 1) % data.length != tail) {  //另一种写法i != tail -1
                res.append( val + ", ");
            }else {
                res.append(val + "] tail. Size: " + String.valueOf(data[i]) + " capacity: " + String.valueOf(getCapacity()));
            }
        }
        return res.toString();
    }

    public static void main(String[] args) {
        // write your code here
        LoopQueue<Integer> loopQueue = new LoopQueue<>();

        for (int i = 0; i < 10; i ++) {
            loopQueue.enQueue(i);
            System.out.println(loopQueue);
            if (i % 3 == 2) {
                loopQueue.deQueue();
                System.out.println(loopQueue);
            }
        }
    }
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值