队列篇之使用数组模拟一个队列

队列是一个有序列表, 可以使用数组实现, 也可以使用链表实现

队列遵守先进先出的原则

1. 下面使用数组模拟一个队列

public class ArrayQueueDemo {
    public static void main(String[] args) {
        ArrayQueue queue = new ArrayQueue(3);
        queue.add(1);
        queue.show();
        System.out.println("-----------------");
        queue.add(2);
        queue.show();
        System.out.println("-----------------");
        queue.add(3);
        queue.show();
        System.out.println("-----------------");
//        System.out.println(queue.pop());
        System.out.println(queue.pop());
////        System.out.println(queue.pop());
//        queue.show();
        System.out.println("-----------------");
        System.out.println(queue.peek());
        System.out.println(queue.peek());
        System.out.println(queue.peek());
        queue.show();
    }
}

/**
 * 使用数组来模拟队列
 * 该代码存在着一个明显的bug,就是数组只能使用一次, 不能复用. 原因很明显,rear指针只有++操作.
 */
class ArrayQueue {
    /**
     * 队列最大容量
     */
    private int maxSize;

    /**
     * 队列头指针
     */
    private int front;

    /**
     * 队列尾指针
     */
    private int rear;

    /**
     * 存放数据的数组
     */
    private int[] arr;

    // 创建队列的构造器
    public ArrayQueue(int maxSize) {
        this.maxSize = maxSize;
        arr = new int[maxSize];
        front = -1;  // 指向队列头部前一个位置
        rear = -1; // 指向队列尾部指针(指队列最后一个数据)
    }

    /**
     * 判断队列是否已满
     *
     * @return
     */
    public boolean isFull() {
        return rear == maxSize - 1;
    }

    public boolean isEmpty() {
        return rear == front;
    }

    public void add(int data) {
        if (isFull()) {
            System.out.println("队列已满");
            return;
        }

        rear++;  // rear指针后移一位
        arr[rear] = data;
    }

    /**
     * 弹出队列头部的数据,
     * 其实该数据还是存在于数组中,只是front指针+1了, 不能再次获取了而已
     *
     * @return 队列头部的数据
     */
    public int pop() {
        if (isEmpty()) {
            throw new RuntimeException("队列为空, 不能取数据");
        }
        front++; // front后移一位
        return arr[front];
    }

    /**
     * 只是获取队列头部的数据,但并不会移除该数据(即是说front指针不会发生变化)
     * @return 队列头部的数据
     */
    public int peek() {
        if (isEmpty()) {
            throw new RuntimeException("队列为空, 不能peek数据");
        }
        return arr[front + 1]; // 为什么加1? 是因为我们默认front指向数组数据的前一位
    }

    public void show() {
        if (isEmpty()) {
            System.out.println("队列空,无数据");
            return;
        }
        for (int i = 0; i < arr.length; i++) {
            System.out.printf("arr[%d] = %s\n", i, arr[i]);
        }
    }
}

2. jdk中ArrayBlockingQueue实现方式

2.1 构造器

  public ArrayBlockingQueue(int capacity) {
        this(capacity, false);
    }
    public ArrayBlockingQueue(int capacity, boolean fair) {
        if (capacity <= 0)
            throw new IllegalArgumentException();
        this.items = new Object[capacity];
        lock = new ReentrantLock(fair);
        notEmpty = lock.newCondition();
        notFull =  lock.newCondition();
    }

很明显, 通过构造器我们可以知道 , ArrayBlockingQueue是一个有界队列 ,其底层使用了一个Object类型的数组items来存储数据.

2.2 添加元素

因为添加元素到队列的底层方法都是使用offer方法,所以我们直接看offer方法源码即可

public boolean offer(E e) {
    checkNotNull(e);
    final ReentrantLock lock = this.lock;
    lock.lock();
    try {
        // 如果队列中的元素个数count等于数据组items的长度,说明队列已满,直接返回false,添加元素失败
        if (count == items.length)
            return false;
        else {
            enqueue(e); // 添加元素
            return true;
        }
    } finally {
        lock.unlock();
    }
}
private void enqueue(E x) {
    final Object[] items = this.items;
    items[putIndex] = x;  // 将元素x放到数组items的putIndex位置
    if (++putIndex == items.length) // 表示items已经满了
        putIndex = 0;  // putIndex重新置为0
    count++; // 队列元素加1
    notEmpty.signal();
}

源码很简单,锁不是本篇的关注点. 值得一提的是 if (++putIndex == items.length) { putIndex = 0; }这行代码挺有意思的, 它解决了数组重复使用问题.

2.3 获取元素

    private E dequeue() {
        final Object[] items = this.items;
        E x = (E) items[takeIndex];// 获取takeIndex位置的元素,从0开始,就是队列的头部
        items[takeIndex] = null;  // 将数组中takeIndex置空
        if (++takeIndex == items.length)  // 如果队列中的元素取完了,takeIndex重置为0,下次继续从头部开始获取元素
            takeIndex = 0;
        count--;  // 队列中的总元素个数减1
        if (itrs != null)   // 没用到, 没细看
            itrs.elementDequeued();
        notFull.signal();
        return x; // 返回取出的元素
    }

2.4 总结

ArrayBlockingQueue底层使用了一个Object类型的数组存储数据, 同时它维护了两个指针putIndex和takeIndex,它们的默认值都是0, 其中putIndex与offer操作相关,即是每添加一个元素,putIndex的值加1, 如果++putIndex等于数组的length,说明队列已满(其实是数组满了), 这时将putIndex重置为0. takeIndex与putIndex类似,不再赘述.

原文地址:https://www.cnblogs.com/z-qinfeng/p/12128026.html

时间: 2024-10-06 19:46:26

队列篇之使用数组模拟一个队列的相关文章

队列——使用数组模拟环形队列

一.思路分析 上一篇讲到用数组模拟队列,数组无法复用,下面讲解了用数组模拟环形队列的方法,采用取模的方式,使得数组可以重复使用. 首先先对front和rear的含义做了一个调整,front指向队列的第一个元素,rear指向队列最后一个元素的后一个位置.队列满的条件是(rear +1) % maxSize = front ,其中maxSize表示队列的容量.假如maxSize=10, 牺牲掉一个位置,front = 0,此时若rear = 9,队列就已经满了.队列为空的条件是rear == fro

使用两个栈模拟一个队列

接着上一篇"使用两个队列模拟一个栈",这里该如何使用两个栈模拟一个队列呢?具体实现如下: 1 public class Queue<E>{ 2 private Stack<E> s1 = null; //s1作为插入栈 3 private Stack<E> s2 = null; //s2作为弹出栈 4 5 public Queue(){ 6 s1 = new Stack<E>(); 7 s2 = new Stack<E>();

数组模拟环形队列

1.为什么要用环形队列? 2.数组模拟环形队列 3.代码实现 package com.queue; import java.util.Scanner; /** * 数组模拟环形队列 * @author nidegui * @create 2019-10-24 11:33 */ public class CircleQueue { public static void main(String[] args) { System.out.println("测试环形队列的案列"); Circl

web前端面试系列 - 数据结构(两个栈模拟一个队列)

一. 用两个栈模拟一个队列 思路一: 1. 一个栈s1作为数据存储,另一个栈s2,作为临时数据存储. 2. 入队时将数据压人s1 3. 出队时将s1弹出,并压人s2,然后弹出s2中的顶部数据,最后再将剩余数据弹出s2,并压人s1. 思路二: 1. 一个栈s1作为数据存储,另一个栈s2,作为临时数据存储. 2. 入队时,判断s1, 是否为空,如果不为空,则将数据直接压入s1, 如果为空,则将s2中的数据全部倒入s1,在将数据压人s1. 3. 出队时,判断s2, 是否为空,如果不为空,则直接弹出s2

两个栈模拟一个队列和两个队列模拟一个栈

此为网易的一道笔试题.到时候秀逗,不知所云.后来研究之后记录下,以备以后经常翻阅. 栈:先进后出 push和pop 队列:先进先出 offer和poll (1)两个栈模拟一个队列 即将先进后出实现先进先出.比较容易理解,只要所有数据先往一个栈里push,然后将该栈中的数据依次pop出来再push进第二个队列,则顺序自然颠倒过来了,则每次pop是从第二个队列中取数据. import java.util.*; public class StackQueue{ private Stack<Intege

两个栈模拟一个队列

两个栈模拟一个队列,1号栈为入队,栈顶表示队尾:2号栈为出队,栈顶表示队首. 入队,直接进1号栈:出队,先判断2号栈是否有元素,有元素就直接弹出栈顶即队首,如果2号栈没有元素,则将1号栈的元素顺序弹出并进2号栈. [cpp] view plaincopy #include <iostream> #include <stack> #include <assert.h> using namespace std; template<typename T> clas

数据结构和算法之栈和队列一:两个栈模拟一个队列以及两个队列模拟一个栈

今天我们需要学习的是关于数据结构里面经常看到的两种结构,栈和队列.可以说我们是一直都在使用栈,比如说在前面递归所使用的的系统的栈,以及在链表倒序输出时介绍的自定义栈类Stack和使用系统的栈进行递归.那么,在这里我们就讲述一下这两个比较具有特色的或者说关系比较紧密的数据结构之间的互相实现问题. 一:两个栈模拟实现一个队列: 栈的特点是先进后出,然而队列的特点是先进先出. public class Queen(Stack s1,Stack s2){ //实现插入的方法 public void ad

如何使用栈模拟一个队列

队列先进先出. 栈先进后出. 最近看到一道面试题,要求用两个栈模拟一个队列,这个问题在一年前遇到过,记录一下. 使用栈模拟队列,一个肯定不行,首先想到用两个栈来模拟队列. 当队列有数据要入队时,我们同时将这个数据入栈A 此时我们认为,第一次向队列中插入数据完成了.则我们将A中的数据弹出,使用B来接收这些值. 就这样将A中的元素全部弹出并且压进B中,这时就会发现,他们顺序逆转,并且如同队列一样,先进的元素到了栈顶,可以先出. 我们让1出队之后,此时我们还想继续向队列中添加数据. 此时,2依然是栈B

03数组模拟环形队列(没有明白)

1,对数组模拟队列的优化,充分利用数组,因此将数组看做是一个环形的(通过取模的方式来实现) 2,分析说明: ①尾索引的下一个为头索引时表示队列满,即将队列容量空出一个作为约定,这个在做判断队列满的时候需要注意 (rear+1) % maxSize == front   满 ②rear == front   空 ③思路如下: 1,front变量的含义做一个调整,front就指向队列的第一个元素,front的初始值是0 2,rear便令的含义做一个调整,rear指向队列最后一个元素的后一个位置,因为