CS61B:循环队列

循环队列

  • 本循环队列使用顺序表与双向链表实现循环队列。
  • 使用junit测试完所有类的方法。
  • 使用泛型使得循环队列可以容纳任意元素。

循环顺序表

数据结构:
在这里插入图片描述

实现方法:

  • public ArrayDeque() 初始化ArrayDeque队列,将nextLast,nextFirst,itemFirst都设置为相同值。
  • public void resize(int capacity) 根据capacity调整队列大小,并将所有元素从itemFirst到最后一个元素移动到新的队列,并更新nextLast,nextFirst,itemFirst的位置信息。
  • public void addFirst(T item)在队列的头部添加新的元素,对于size为0特殊处理。
  • public void addLast(T item)在队列的尾部添加新的元素,对size为0特殊处理。
  • public boolean isEmpty() 判断队列是否为空,为空返回true。
  • public int size()返回当前队列的大小,通过成员变量size避免迭代。
  • public void printDeque()打印当前对列中的所有元素,元素间用空格分隔,输出完毕后,输出回车。
  • public T removeFirst()删除当前队列头部元素,当队列的使用率小于等于0.25时,调整队列大小,当size为0特殊处理。
  • public T removeLast()删除当前队列尾部元素,当队列的使用率小于等于0.25时,调整队列大小,当size为0特殊处理。
  • public T get(int index)获取当前队列index上的元素,由于队列的头部不确定,所以通过循环查找元素位置。
public class ArrayDeque<T> {
    private T[] items;
    private int size;
    private int RFACTOR = 2;
    private int itemFirst;
    private int nextFirst;
    private int nextLast;
    public ArrayDeque() {
        items = (T[]) new Object[8];
        size = 0;
        nextLast = 0;
        nextFirst = 0;
        itemFirst = 0;
    }

    public void resize(int capacity) {
        T[] a = (T[]) new Object[capacity];
        int index = itemFirst;
        for (int i = 0; i < size; i++) {
            a[i] = items[index];
            index = (index + 1) % items.length;
        }
        itemFirst = 0;
        nextFirst = capacity - 1;
        nextLast = size;
        items = a;
    }

    /**
     * Adds an item of type T to the front of the deque.
     * @param item
     */
    public void addFirst(T item) {
        if (size >= items.length) {
            resize(size * RFACTOR);
        }
        if (size == 0) {
            items[itemFirst] = item;
            nextFirst = (itemFirst - 1 + items.length) % items.length;
            nextLast = (itemFirst + 1) % items.length;
            size++;
            return;
        }
        items[nextFirst] = item;
        nextFirst = (nextFirst - 1 + items.length) % items.length;
        itemFirst = (itemFirst - 1 + items.length) % items.length;
        size++;
    }

    /**
     * Adds an item of type T to the back of the deque.
     * @param item
     */
    public void addLast(T item) {
        if (size >= items.length) {
            resize(size * RFACTOR);
        }
        if (size == 0) {
            items[itemFirst] = item;
            nextFirst = (itemFirst - 1 + items.length) % items.length;
            nextLast = (itemFirst + 1) % items.length;
            size++;
            return;
        }
        items[nextLast] = item;
        nextLast = (nextLast +1) % items.length;
        size++;
    }

    /**
     * Returns true if deque is empty, false otherwise.
     * @return
     */
    public boolean isEmpty() {
        if (size == 0) {
            return true;
        }
        return false;
    }

    /**
     * Returns the number of items in the deque.
     * @return
     */
    public int size() {
        return size;
    }

    /**
     *  Prints the items in the deque from first to last, separated by a space. Once all the items have been printed, print out a new line.
     */
    public void printDeque() {
        int index = itemFirst;
        for (int i = 0; i < size; i++) {
            System.out.print(items[index]+" ");
            index = (index + 1) % items.length;
        }
        System.out.println();
    }

    /**
     * Removes and returns the item at the front of the deque. If no such item exists, returns null.
     */
    public T removeFirst() {
        if ((double) size / items.length <= 0.25) {
            resize(items.length / RFACTOR);
        }
        if (size == 0) {
            return null;
        }
        size--;
        if (size == 0) {
            nextFirst = itemFirst;
            nextLast = itemFirst;
            return items[itemFirst];
        } else {
            itemFirst = (itemFirst + 1) % items.length;
            nextFirst = (nextFirst + 1) % items.length;
            return items[nextFirst];
        }

    }

    /**
     * Removes and returns the item at the back of the deque. If no such item exists, returns null.
     * @return
     */
    public T removeLast() {
        if ((double) size / items.length <= 0.25) {
            resize(items.length / RFACTOR);
        }
        if (size == 0) {
            return null;
        }
        size--;
        if (size == 0) {
            nextFirst = itemFirst;
            nextLast = itemFirst;
            return items[itemFirst];
        } else {
            nextLast = (nextLast - 1 + items.length) % items.length;
            return items[nextLast];
        }

    }

    /**
     * Gets the item at the given index, where 0 is the front, 1 is the next item, and so forth. If no such item exists, returns null. Must not alter the deque!
     * @param index
     * @return item
     */
    public T get(int index) {
        int k = itemFirst;
        for (int i = 0; i < index; i++) {
            k = (k+1) % items.length;
        }
        return items[k];
    }
}

双向循环链表

数据结构
在这里插入图片描述
实现方法:

  • public LinkedListDeque()初始化一个空的循环双向链表队列
  • public LinkedListDeque(LinkedListDeque other)创建一个与other队列相同的循环队列副本。
  • public void addFirst(T item)在循环队列的头部添加一个新的元素。
  • public void addLast(T item)在循环队列的尾部添加一个新的元素。
  • public boolean isEmpty()判断当前循环队列是否为空。
  • public int size()返回当前链表的大小。
  • public void printDeque()打印当前循环队列的所有元素,元素之间以空格分隔,输出完毕后输出空格。
  • public T removeFirst()删除队列中的头部元素。
  • public T removeLast()删除队列中的尾部元素。
  • public T get(int index)获取当前队列中的index索引处的元素,通过迭代的方式。
  • public T getRecursive(int index)获取当前队列中的index索引出的元素,通过递归的方式。
  • public Iterator<T> iterator()实现Iterable接口的iterator方法,使得循环队列可以用java中foreach语法。
  • public boolean equals(Object obj)复写object类中的equals方法,使得循环队列有正确的比较相等操作。
import java.util.Iterator;
import java.util.NoSuchElementException;

public class LinkedListDeque<T> implements Iterable<T> {
    private DequeNode first;

    private int size;
    public LinkedListDeque() {
        first = null;
        size = 0;
    }

    public LinkedListDeque(LinkedListDeque other) {
        if (other.first == null && other.size() == 0) {
            first = null;
            size = 0;
        }else {
            first = null;
            size = 0;
            for (DequeNode p = other.first; p != other.first.prev; p = p.next) {
               addLast(p.item);
            }
            addLast((T) other.first.prev.item);
        }
    }

    private class DequeNode {
        private T item;
        private DequeNode prev;
        private DequeNode next;

        DequeNode(T i, DequeNode p, DequeNode n) {
            item = i;
            prev = p;
            next = n;
        }
    }


    /**
     * Adds an item of type T to the front of the deque.
     * @param item
     */
    public void addFirst(T item){
        if (first == null) {
            first = new DequeNode(item, null, null);
            first.next = first;
            first.prev = first;
        } else {
            first = new DequeNode(item, first.prev, first);
            first.next.prev = first;
        }
        size++;
    }

    /**
     * Adds an item of type T to the back of the deque.
     * @param item
     */
    public void addLast(T item){
        if (first == null) {
            first = new DequeNode(item, null, null);
            first.next = first;
            first.prev = first;
        } else {
            first.prev = new DequeNode(item, first.prev,first);
            first.prev.prev.next = first.prev;
        }
        size++;
    }

    /**
     * Returns true if deque is empty, false otherwise.
     * @return
     */
    public boolean isEmpty(){
        if (size == 0) {
            return true;
        } else {
            return false;
        }
    }

    /**
     * Returns the number of items in the deque.
     * @return
     */
    public int size(){
        return size;
    }

    /**
     *  Prints the items in the deque from first to last, separated by a space. Once all the items have been printed, print out a new line.
     */
    public void printDeque(){
        for (DequeNode p = first; p != first.prev; p= p.next) {
            System.out.print(p.item+" ");
        }
        System.out.print(first.prev.item + " ");
        System.out.println();
    }


    /**
     * Removes and returns the item at the front of the deque. If no such item exists, returns null.
     */
    public T removeFirst(){
        if (isEmpty()) {
            return null;
        }
        size--;
        DequeNode p = first;
        first.prev.next = first.next;
        first.next.prev = first.prev;
        first = first.next;
        return p.item;
    }

    /**
     * Removes and returns the item at the back of the deque. If no such item exists, returns null.
     * @return
     */
    public T removeLast(){
        if (isEmpty()) {
            return null;
        }
        size--;
        DequeNode p = first.prev;
        first.prev.prev.next = first;
        first.prev = first.prev.prev;
        return p.item;
    }

    /**
     * Gets the item at the given index, where 0 is the front, 1 is the next item, and so forth. If no such item exists, returns null. Must not alter the deque!
     * @param index
     * @return item
     */
    public T get(int index){
        if (index < 0 || index >= size) {
            return null;
        }
        DequeNode p = first;
        for (int i = 0; i < index; i++) {
            p = p.next;
        }
        return p.item;
    }

    private T getRecursiveHelp(int index, DequeNode p) {
        if (index == 0) {
            return p.item;
        }
        return getRecursiveHelp(index-1, p.next);
    }

    /**
     * Gets the item at the given index, where 0 is the front, 1 is the next item, and so forth. If no such item exists, returns null. Must not alter the deque!
     * @param index
     * @return
     */
    public T getRecursive(int index) {
        if (index < 0 || index >= size) {
            return null;
        }
        return getRecursiveHelp(index, first);
    }


    private class DequeIterator implements Iterator<T>{
        private DequeNode current = first;
        int i = 0;

        @Override
        public boolean hasNext() {
            if (i < size) {
                i++;
                return true;
            }
            return false;
        }

        @Override
        public T next() {
            T item = current.item;
            current = current.next;
            return item;
        }
    }

    @Override
    public Iterator<T> iterator() {
        return new DequeIterator();
    }

    @Override
    public boolean equals(Object obj) {
        if (this == obj) return true;
        if (!(obj instanceof LinkedListDeque)) return false;
        LinkedListDeque<?> other = (LinkedListDeque<?>) obj;
        if (this.size != other.size) return false;
        Iterator<T> thisIt = this.iterator();
        Iterator<?> otherIt = other.iterator();
        while (thisIt.hasNext() && otherIt.hasNext()) {
            if (!thisIt.next().equals(otherIt.next())) {
                return false;
            }
        }
        return true;
    }
}

循环队列实现的对比

循环链表队列(LinkedListDeque)

优点:
动态扩展: 链表队列可以根据需要动态地增加节点,不需要事先分配固定的存储空间。
内存利用: 在任何时候,链表都只占用它实际需要的内存空间。
复杂度: 在链表的头部和尾部添加或删除元素都是常数时间的操作(O(1)),因为不需要移动其他元素。
缺点:
内存开销: 每个元素需要额外存储指针信息,这增加了内存的开销。
时间开销: 访问链表中间的元素需要从头节点或尾节点遍历,因此访问时间为线性时间(O(n))。
缓存不友好: 链表的节点可能在内存中不连续,导致缓存命中率降低,可能影响性能。

循环数组队列(ArrayDeque)

优点:
随机访问: 由于数据连续存储,支持快速随机访问,访问任意元素的时间复杂度为O(1)。
内存连续: 数据存储在连续的内存空间中,有利于提高缓存的利用率。
空间预分配: 初始时即分配一个数组,可以减少内存的频繁分配。
缺点:
固定容量: 初始需要定义数组的大小,虽然可以动态扩容,但这是通过创建新数组并复制旧数组元素来实现的,耗费时间和空间。
扩容成本: 扩容操作涉及到内存的重新分配及数据的复制,特别是当队列大小达到数组容量时,每次添加元素都可能触发扩容,这可能导致较大的性能开销。
可能的空间浪费: 如果队列的使用量远小于数组的实际分配大小,会造成空间浪费。

评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

poison_Program

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值