ADT: Queue 队列

ADT: Queue 队列

简介

上一篇ADT: Stack 栈介绍了Stack(栈)的数据结构,这一篇来介绍与栈相似的Queue(队列)的数据结构,与栈相同的是都能够向其添加(push, enqueue)取出(pop, dequeue)元素;不同的是栈的出入都从容器的同一边(LIFO),而队列则是两边开口,一边进一边出,接下来就让我们来看看队列是怎么实现的吧。

参考

正文

队列结构

队列是一个拥有两个开口的容器,一边作为输入(enqueue)端,一边为输出(dequeue)端,属于具有特殊结构的线性表,限定只能从队尾加入元素,从队首取出元素。

抽象接口

队列的抽象数据结构对外开放的接口有五个:

ENQUEUE(x)
入队,将元素 x 从队尾加入到队列中

DEQUEUE()
出队,将队首元素弹出并返回

FRONT() / getFirst()
取队首,获取队首元素,也就是当前队列下一个可以出队的元素

BACK() / getLast()
取队尾,获取队尾元素,也就是最近一次入队的元素

EMPTY()
检查队列是否为空,即队列中是否存在任意元素

实现要素

队列也能够分别使用数组和链表来实现,而通常我们偏好使用环形队列(环状数组or环状链表),指定特定长度的容器后,使指针在环内循环,本篇介绍使用环形数组实现的队列

数组实现的队列需要一下要素:

  1. 保存元素的数组 T[] Q
  2. 记录当前数组的大小 int size
  3. 记录队首位置 int front
  4. 记录队尾位置 int rear
  5. 标记队列是否为满 boolean full (不使用则大小为 n 的 Q 只能保存 n - 1 个元素)

Java 实现(使用范型)

  • Queue.java
/**
 * 队列
 * @param <T>
 */
public interface Queue<T> {

    /**
     * 入队
     * @param t
     */
    void enqueue(T t);

    /**
     * 出队
     * @return
     */
    T dequeue();

    /**
     * 获取队首元素
     * @return
     */
    T getFirst();

    /**
     * 获取队尾元素
     * @return
     */
    T getLast();

    /**
     * 判断队列是否为空
     * @return
     */
    boolean empty();
}

  • QueueCircular.java
public class QueueCircular<T> implements Queue<T> {

    public QueueCircular() {
        this(10);
    }

    public QueueCircular(int size) {
        this.Q = (T[]) new Object[size];
        this.front = this.rear = 0;
        this.full = false;
    }

    private T[] Q;
    private int front;
    private int rear;
    private boolean full;

    /**
     * 入队
     *
     * @param t
     */
    public void enqueue(T t) {
        if (full) return;
        Q[rear] = t;
        rear = (rear + 1) % Q.length;
        if (rear == front) full = true;
    }

    /**
     * 出队
     *
     * @return
     */
    public T dequeue() {
        if (front == rear && !full) return null;
        if (full) full = false;
        T res = Q[front];
        front = (front + 1) % Q.length;
        return res;
    }

    /**
     * 获取队首元素
     *
     * @return
     */
    public T getFirst() {
        return empty() ? null : Q[front];
    }

    /**
     * 获取队尾元素
     *
     * @return
     */
    public T getLast() {
        return empty() ? null : Q[(rear - 1 + Q.length) % Q.length];
    }

    /**
     * 判断队列是否为空
     *
     * @return
     */
    public boolean empty() {
        return front == rear && !full;
    }
}
  • QueueTest.java

public class QueueTest {
    @Test
    public void test_queue_circular() {
        Queue<Integer> queue = new QueueCircular<Integer>();
        // 入队 11 个数
        for (int i = 1; i < 22; i += 2) {
            queue.enqueue(i);
        }
        // 出队 11 个数,检查是否符合 FIFO 顺序
        for (int i = 1; i < 20; i += 2) {
            assertEquals((Integer) i, queue.dequeue());
        }
        // 查看指针问题
        for (int i = 0; i < 10; i++) {
            queue.enqueue(i);
            assertEquals((Integer) i, queue.dequeue());
        }
        for (int i = 1; i < 20; i += 2) queue.enqueue(i);
        // 查看临界条件下的出入栈
        for (int i = 1; i < 20; i += 2) {
            assertEquals((Integer) i, queue.dequeue());
            queue.enqueue(i);
        }
    }
}

C++ 实现(使用模版类)

  • queue.h
template<class T>
class Queue {
public:
    Queue();
    Queue(int);
    ~Queue();
    void enqueue(T);
    T dequeue();
    T getFirst();
    T getLast();
    bool empty();

private:
    T *Q;
    int size;
    int front;
    int rear;
    bool full;
};

template<class T>
Queue<T>::Queue(): Q(new T[10]), size(10), front(0), rear(0), full(false) {}

template<class T>
Queue<T>::Queue(int size) : Q(new T[size]), size(size), front(0), rear(0), full(false) {}

template<class T>
Queue<T>::~Queue() {
    delete[] Q;
}

template<class T>
void Queue<T>::enqueue(T t) {
    if (full) return;
    Q[rear] = t;
    rear = (rear + 1) % size;
    if (rear == front) full = true;
}

template<class T>
T Queue<T>::dequeue() {
    if (front == rear && !full) return 0;
    if (full) full = false;
    T res = Q[front];
    front = (front + 1) % size;
    return res;
}

template<class T>
T Queue<T>::getFirst() {
    return empty() ? 0 : Q[front];
}

template<class T>
T Queue<T>::getLast() {
    return empty() ? 0 : Q[(rear - 1 + size) % size];
}

template<class T>
bool Queue<T>::empty() {
    return front == rear && !full;
}
  • myassert.h
#include <iostream>

template<class T>
void assertEquals(T expected, T actual) {
    if (expected != actual) {
        cout << "AssertEquals Error:" << endl;
        cout << "\texpected: " << expected << endl;
        cout << "\tactual: " << actual << endl;
        throw "";
    }
}
  • queue_test.cpp
#include "queue_test.h"
#include "queue.h"
#include "myassert.h"

void test_queue_circular() {

    Queue<int> Q;
    for (int i = 1; i < 22; i += 2) {
        Q.enqueue(i);
    }

    // 出队 11 个数,检查是否符合 FIFO 顺序
    for (int i = 1; i < 20; i += 2) {
        assertEquals(i, Q.dequeue());
    }

    // 查看指针问题
    for (int i = 0; i < 10; i++) {
        Q.enqueue(i);
        assertEquals(i, Q.dequeue());
    }

    for (int i = 1; i < 20; i += 2) Q.enqueue(i);
    // 查看临界条件下的出入栈
    for (int i = 1; i < 20; i += 2) {
        assertEquals(i, Q.dequeue());
        Q.enqueue(i);
    }
    cout << "test queue circular success" << endl;
}

结语

队列跟栈非常类似,不过栈是后进先出(LIFO)的结构,而队列则是先进先出(FIFO)。借由这个 FIFO 的特性,我们能够在一些场景维护一个队列来维持按序处理任务的算法模型,如树的按层遍历进程调度就绪队列等应用场景。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值