先入先出的数据结构

算法与数据结构之队列和栈_循环队列

在 FIFO 数据结构中,将首先处理添加到队列中的第一个元素。

如上图所示,队列是典型的 FIFO 数据结构。插入(insert)操作也称作入队(enqueue),新元素始终被添加在队列的末尾。 删除(delete)操作也被称为出队(dequeue)。 你只能移除第一个元素。

实现队列:

通过一个动态数组和一个指向队首的索引就可以实现。实现的思路很简单,只需要实现入队和出队的操作,入队操作只用在动态数组后添加元素即可,出队操作需要先判断队列是否为空,非空的时候只用将队首的索引加一即可,

在判断队列是否为空时只用判断索引和这个动态数组的大小,因为这个动态数组的大小不会改变,因此只要这个索引小于数组大小就证明队列中有元素。

缺点:随着队列的元素的增加,指针的往后移动,会存在很大的空间浪费。

class QueueClass {
    // store elements
    private List<Integer> data;
    // a pointer to indicate the start position
    private int p_start;
    public QueueClass() {
        data = new ArrayList<Integer>();
        p_start = 0;
    }
    /** Insert an element into the queue. Return true if the operation is successful. */
    public boolean enQueue(int x) {
        data.add(x);
        return true;
    };
    /** Delete an element from the queue. Return true if the operation is successful. */
    public boolean deQueue() {
        if (isEmpty() == true) {
            return false;
        }
        p_start++;
        return true;
    }
    /** Get the front item from the queue. */
    public int Front() {
        return data.get(p_start);
    }
    /** Checks whether the queue is empty or not. */
    public boolean isEmpty() {
        return p_start >= data.size();
    }
};

class Main {
    public static void main(String[] args) {
        QueueClass q = new QueueClass();
        q.enQueue(5);
        q.enQueue(3);
        if (q.isEmpty() == false) {
            System.out.println(q.Front());
        }
        q.deQueue();
        if (q.isEmpty() == false) {
            System.out.println(q.Front());
        }
        q.deQueue();
        if (q.isEmpty() == false) {
            System.out.println(q.Front());
        }
    }
}

 

循环队列

此前,我们提供了一种简单但低效的队列实现。

更有效的方法是使用循环队列。 具体来说,我们可以使用固定大小的数组和两个指针来指示起始位置和结束位置。 目的是重用我们之前提到的被浪费的存储。

 实现的关键点在于几点:

1、首先需要有出队和入队两个操作。还需要有判断队列为空或者为满的操作。因为是循环队列,因此在移动指针时需要进行取模计算以便达到循环的数据结构

2、在队列满的时候通常是入队的时候,然而入队是通过增加队尾指针的位置,因此判断队列为满就是队尾的指针将要和队首的指针重合。即(tail+1)%size == head;

3、在队列为空的时候,一般有两种情况,第一种情况是最开始的时候,还没有任何元素的时候,另外一种情况就是出队时所有元素都出完了。然而第二种情况的临界点就是只剩一个元素的时候,这时候

head==tail, 第一种情况时的head==tail,因此只用在出完最后一个元素的时候将head和tail设置为初始值-1即可。

4、在入队时需要判断队列是否为满,还有判断是否为空,因为如果不为空只用移动队尾的指针就行,但是如果为空还要移动队首的指针

5、在出队的时候需要判断是否还有元素,如果只剩最后一个元素则需要将队首和队尾的指针设置为初始的一个状态。

class MyCircularQueue {

    private int[] data;
    private int head;
    private int tail;
    private int size;

    /** Initialize your data structure here. Set the size of the queue to be k. */
    public MyCircularQueue(int k) {
       data = new int[k];
       head = -1;
       tail = -1;
       size = k;
    }

    /** Insert an element into the circular queue. Return true if the operation is successful. */
    public boolean enQueue(int value) {
        if(isFull()) return false;
        if(isEmpty()) {
            head = 0;
        }
        tail = (tail+1)%size;
        data[tail] = value;
        return true;
    }

    /** Delete an element from the circular queue. Return true if the operation is successful. */
    public boolean deQueue() {

        if(isEmpty()) return false;

        if(tail == head){
            head = -1;
            tail = -1;
            return true;
        }
        head = (head+1)%size;
        return true;
    }

    /** Get the front item from the queue. */
    public int Front() {
        if(isEmpty()) return -1;
        return data[head];
    }

    /** Get the last item from the queue. */
    public int Rear() {
        if(isEmpty()) return -1;
        return data[tail];
    }

    /** Checks whether the circular queue is empty or not. */
    public boolean isEmpty() {
        return tail == -1;
    }

    /** Checks whether the circular queue is full or not. */
    public boolean isFull() {
     if((tail+1)%size ==head) return true;
     else return false;
    }