java基础复习——队列

队列概念

复习了一下队列这种数据结构,队列这种数据结构起始挺好理解的:将它想象成奶茶店排队买奶茶的人,先排队的,排在队伍前面,先买到奶茶喝,后来排队的都在队伍后边,最后买到奶茶。

先进先出,后进后出(FIFO)

所以,队列是一种操作受限的数据结构。

在这里插入图片描述


队列实现

    • 顺序队列

顺序队列,顾名思义数据在队列中顺序存储。这样的特性,与数组的存储方式不谋而合,因此我们可以使用数组来实现顺序队列。

class ArrayQueue {
    private Object[] mData; // 数组
    private int mLength; // 数组大小

    private int mHead;
    private int mTail;

    public ArrayQueue(int length) {
        mLength = length;
        mData = new Object[length];
    }

    /**
     * 入队
     *
     * @param item
     * @return
     */
    boolean enqueue(Object item) {
        if (mTail == mLength) // 队列满
            return false;
        mData[mTail] = item;
        mTail++;
        return true;
    }

    /**
     * 出队
     *
     * @return
     */
    Object dequeue() {
        if (mHead == mTail) // 空队列
            return null;
        Object result = mData[mHead];
        mHead++;
        return result;
    }
}

从上面的代码,来看队列这种数据结构实现的思路:
首先需要一个顺序存储容器-数组,来存储「队列」内容,然后定义两个下标指针,分别指向队列的头部和尾部。
可以根据下图来帮助理解。

在这里插入图片描述

入队的元素,添加到数组下标为「tail」的位置,同时「tail」移动到新的队伍尾部位置。所以,队尾下标指针「tail」总是在队列最后一个元素的下一个位置,因此我们可以使用「tail==mLength」来判断队列是否已满。

那么用上面的方法「tail==mLength」来判断队列是否已满,可能出现这种情况:

在这里插入图片描述

队头下标指针「mHead」前面空出的位置并没有填充数据,因而此时队列并没有满。

那么针对这种情况,就需要「搬移」数据:将「mHead」和「mTail」之间的数据,搬移到数组下标「0」到「mTail-mHead」的位置。

    /**
     * 入队
     *
     * @param item
     * @return
     */
    boolean enqueue(Object item) {
        // tail == n 表示队列末尾没有空间了
        if (mTail == mLength) {
            // tail ==n && head==0,表示整个队列都占满了
            if (mHead == 0) return false;
            // 数据搬移
            System.arraycopy(mData, mHead, mData, 0, mTail - mHead);
            // 搬移完之后重新更新 head 和 tail
            mTail -= mHead;
            mHead = 0;
        }

        mData[mTail] = item;
        ++mTail;
        return true;
    }
    • 链表队列

使用链表实现的队列,称之为链表队列。

链表队列的大小没有限制,因此不需要设置队列大小,只添加链表头部和尾部指针:head指针和tail指针。它们分别指向链表的第一个结点和最后一个结点。

入队:tail.next = newNode, tail = tail.next;
出队:head = head.next.

直接上代码:

class ListQueue {
    private Node mHead;
    private Node mTail;

    /**
     * 入队
     *
     * @param item
     */
    void enqueue(Object item) {
        if (null == mTail) { // 空队入队
            Node newNode = new Node(item, null);
            mHead = newNode;
            mTail = newNode;
        } else {
            mTail.mNext = new Node(item, null);
            mTail = mTail.mNext;
        }
    }

    /**
     * 出队
     *
     * @return
     */
    Object dequeue() {
        if (null == mHead)
            return null;
        Object result = mHead.mData;
        mHead = mHead.mNext;
        if (null == mHead) {
            mTail = null;
        }
        return result;
    }

    /**
     * 打印队列内容
     */
    void printAll() {
        Node node = mHead;
        while (null != node) {
            System.out.print(node.mData + " ");
            node = node.mNext;
        }
        System.out.println();
    }

    class Node {
        private Object mData;
        private Node mNext;

        public Node() {
            this.mData = null;
            this.mNext = null;
        }

        public Node(Object data) {
            mData = data;
        }

        public Node(Object data, Node next) {
            mData = data;
            mNext = next;
        }
    }
}
    • 循环队列

在第1节中,数组满的判断条件「tail==mLength」时,存在队列前半部分由于出队导致实际队列并不满的情况。我们的处理方式是搬移数据到数组头部位置。

这样的搬移操作,在数据量很大的情况下n,它的时间复杂度为O(n)。因此并不是一个十分有效的解决方法。这节使用另外一种解决思路——循环队列,来解决这个效率问题。

循环队列,也就是说队列是类似一个圆环一样的环形结构:队尾下标指针「tail」在判断队满的时候「tail==mLength」,此时「tail」下标指针的下一个位置为数组下标为0的位置,形成收尾相接的形状。

在这里插入图片描述

通过这样的方法,就可以避免搬移数据的操作。

循环链表实现的关键在于判断队空和队满的条件

队空的判断条件还是「head==tail」

队满的判断条件:从循环队列的实现思路,可以看到,「tail」指针的变化就是基于数组长度mLength的一个循环,这在代码中通常会用取余「%」来实现。而队空的判断条件使用了「head==tail」,那么队满的判断则是「tail+1 == head」。再基于循环取余%的运算,得到判断条件:

在这里插入图片描述

(tail+1)%n=head

所以,最终的实现代码:

class CircleQueue {
    private Object[] mData; // 数组
    private int mLength; // 数组大小

    private int mHead;
    private int mTail;

    public CircleQueue(int length) {
        mLength = length;
        mData = new Object[length];
    }

    /**
     * 入队
     *
     * @param item
     * @return
     */
    boolean enqueue(Object item) {
        int nextTail = (mTail + 1) % mLength; // 队尾的下一个位置
        if (nextTail == mHead) // 队列满
            return false;
        mData[mTail] = item;
        mTail = nextTail;
        return true;
    }

    /**
     * 出队
     * @return
     */
    Object dequeue() {
        if (mHead == mTail) // 空队列
            return null;
        Object result = mData[mHead];
        mHead = (mHead + 1) % mLength;
        return result;
    }
}

数据结构队列的复习完成。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值