数据结构-队列(queue)的介绍与代码实现


一、队列是什么?

这里引入百度百科的解释:

  • 队列是一种特殊的线性表,特殊之处在于它只允许在表的前端(front)进行删除操作,而在表的后端(rear)进行插入操作,和栈一样,队列是一种操作受限制的线性表。
  • 进行插入操作的端称为队尾,进行删除操作的端称为队头。
  • 通俗的理解:一种先进先出的数据结构

队列上溢:队列的元素数量超出队列的容量
队列下溢:对空队列执行出队操作

在这里插入图片描述

二、队列的接口

代码如下(示例):

public interface Queue<E> {
    void enqueue(E e);

    E dequeue();

    E getFront();

    int getSize();

    boolean isEmpty();
}

三、实现队列的接口


1.通过动态数组实现

动态数组的底层实现可参考:
https://blog.csdn.net/GGB__/article/details/120248300

代码如下(示例):

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 void enqueue(E e) {
        array.addLast(e);
    }

    @Override
    public E dequeue() {
        return array.removeFirst();
    }

    @Override
    public E getFront() {
        return array.get(0);
    }

    @Override
    public int getSize() {
        return array.getSize();
    }

    @Override
    public boolean isEmpty() {
        return array.isEmpty();
    }

    public int getCapacity() {
        return array.getCapacity();
    }

    @Override
    public String toString() {
        return "出队ArrayQueue:" + array + "入队";
    }
}

2.通过两个栈实现

栈的底层实现可参考:
https://blog.csdn.net/GGB__/article/details/120250106

实现思想:

  • 利用栈先进后出的性质

  • 两个栈 一个栈负责处理入队

  • 另一个栈负责处理出队

  • 出队操作时:

  • 将负责入队操作的栈执行出栈操作

  • 将出栈的元素放置于负责出队操作的栈中

  • 直至负责入队操作的栈为空,再对负责出队操作的栈执行出栈操作,进而实现出队操作。

  • 入队操作时:

  • 如果负责出队操作的栈不为空,则先将该栈的所有元素执行出栈,存放在负责入队操作的栈中,再执行入队操作

代码如下(示例):

public class ArrayStack2<E> {

    private ArrayStack<E> stack1 = new ArrayStack<>();
    private ArrayStack<E> stack2 = new ArrayStack<>();

    public void push(E e) {

        while (!stack2.isEmpty()) {
            stack1.push(stack2.pop());
        }
        stack1.push(e);
    }

    ;

    public E pop() {
        if (isEmpty()) {
            return null;
        }
        while (!stack1.isEmpty()) {
            stack2.push(stack1.pop());
        }
        return stack2.pop();
    }

    ;

    public E getFront() {

        if (isEmpty()) {
            return null;
        }

        while (!stack1.isEmpty()) {
            stack2.push(stack1.pop());
        }
        return stack2.pop();
    }

    public boolean isEmpty() {
        return stack1.isEmpty() && stack2.isEmpty();
    }

    public E peek() {
        while (!stack1.isEmpty()) {
            stack2.push(stack1.pop());
        }
        return stack2.peek();
    }

    @Override
    public String toString() {
        while (!stack2.isEmpty()) {
            stack1.push(stack2.pop());
        }
        return "ArrayStack2:出队" + stack1 + "入队";
    }
}

3.测试

代码如下(示例):

public static void main(String[] args) {
        Queue<Integer> arrayQueue = new ArrayQueue<>(10);
        for (int i = 0; i < 10; i++) {
            arrayQueue.enqueue(i);
        }
        System.out.println(arrayQueue);
        System.out.println(arrayQueue.dequeue());
        System.out.println(arrayQueue);
        System.out.println(arrayQueue.getFront());
        System.out.println(arrayQueue);
    }

测试结果如下(示例):

出队ArrayQueue:Array:size=10, capacity=10
[0,1,2,3,4,5,6,7,8,9]入队
0
出队ArrayQueue:Array:size=9, capacity=10
[1,2,3,4,5,6,7,8,9]入队
1
出队ArrayQueue:Array:size=9, capacity=10
[1,2,3,4,5,6,7,8,9]入队


四、循环队列

简单介绍

  1. 循环队列就是将队列存储空间的最后一个位置绕到第一个位置,形成逻辑上的环状空间,供队列循环使用。
  2. 在循环队列结构中,当存储空间的最后一个位置已被使用而再要进入队运算时,只需要存储空间的第一个位置空闲,便可将元素加入到第一个位置,此时的front = (front+1)%capacity 即将存储空间的第一个位置作为队尾。
  3. 循环队列可以更简单防止伪溢出的发生,但队列大小是固定的。
    在循环队列中,当队列为空时,有front=tail,而当所有队列空间全占满时,也有front=tail。
    为了区别这两种情况,规定循环队列最多只能有capacity-1个队列元素,当循环队列中只剩下一个空存储单元时,队列就已经满了。
    因此,队列判空的条件是front=tail,而队列判满的条件是front=(tail+1)%capacity.

一张图了解循环队列这里是引用

代码实现:

package com.lingo.queue;

/**
 * 这里循环队列的尾指针空出一个位置(以空间换时间的思想)
 *
 * @param <E>
 */
public class LoopQueueT<E> implements Queue<E> {

    //底层数组
    private E[] data;

    //头指针
    private int front;

    //尾指针
    private int tail;

    private int size;

    public LoopQueueT(int capacity) {
        data = (E[]) new Object[capacity + 1];
        front = 0;
        tail = 0;
        size = 0;
    }

    public LoopQueueT() {
        this(10);
    }

    @Override
    public void enqueue(E e) {
        if ((tail + 1) % data.length == front) {
            reSize(data.length * 2);
        }
        data[tail] = e;
        //是预防数组后无元素空间,前面有空间
        tail = (tail + 1) % data.length;
        size++;
    }

    @Override
    public E dequeue() {
        if (isEmpty()) {
            throw new IllegalArgumentException("队列为空!");
        }
        E e = data[front];
        data[front] = null;
        front = (front + 1) % data.length;
        size--;
        //自动缩容
        //这里不能是data.length/2 ==> 如果是size=5 capacity=11 导致无法存留一个空的空间给tail
        if (size == data.length / 4 && data.length / 2 != 0) {
            reSize(data.length / 2);
        }
        return e;
    }

    //采用从尾加从头出
    @Override
    public E getFront() {
        if (isEmpty()) {
            throw new IllegalArgumentException("队列为空!");
        }
        E e = data[front];
        return e;
    }

    @Override
    public int getSize() {
        return size;
    }

    public int getCapacity() {
        return data.length - 1;
    }

    /**
     * 自动扩容/缩容
     *
     * @param newCapacity
     */
    private void reSize(int newCapacity) {

        E[] newData = (E[]) new Object[newCapacity];
        for (int i = 0; i < size; i++) {
            newData[i] = data[(front + i) % data.length];
        }
        data = newData;
        front = 0;
        tail = size;
    }

    @Override
    public boolean isEmpty() {
        return front == tail;
    }

    public static void main(String[] args) {

        LoopQueueT<Integer> loopQueueT = new LoopQueueT<>();
        loopQueueT.enqueue(11);
        loopQueueT.enqueue(10);
        loopQueueT.enqueue(4);
        loopQueueT.enqueue(6);
        System.out.println("容量:" + loopQueueT.getCapacity());
        System.out.println("出队操作:" + loopQueueT.dequeue());
        System.out.println("容量:" + loopQueueT.getCapacity());
        System.out.println("出队操作:" + loopQueueT.dequeue());
        System.out.println("容量:" + loopQueueT.getCapacity());
        System.out.println("出队操作:" + loopQueueT.dequeue());
        System.out.println("容量:" + loopQueueT.getCapacity());

    }
}

输出结果:
容量:10
出队操作:11
容量:10
出队操作:10
容量:4
出队操作:4
容量:1

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值