数据结构和算法(七)--循环队列(CircleQueue)和双端循环队列(CircleDeque)

数据结构和算法(七)–循环队列(CircleQueue)和双端循环队列(CircleDeque)

什么是循环队列

  • 为充分利用向量空间,克服"假溢出"(系统作为队列用的存储区还没有满,但队列却发生了溢出,我们把这种现象称为"假溢出"。)现象的方法是:将向量空间想象为一个首尾相接的圆环,并称这种向量为循环向量。
  • 存储在其中的队列称为循环队列(Circular Queue)。循环队列是把顺序队列首尾相连,把存储队列元素的表从逻辑上看成一个环,成为循环队列。
  • 在循环队列结构中,当存储空间的最后一个位置已被使用而再要进入队运算时,只需要存储空间的第一个位置空闲,便可将元素加入到第一个位置,即将存储空间的第一个位置作为队尾。
  • 循环队列可以更简单防止伪溢出的发生,但队列大小是固定的。

循环队列的特点

  • 循环队列和队列大同小异,里面有一个frontIndex指向头节点的位置索引(使用数组实现时)
  • 当头部删除了元素之后,位置会空出来,frontIndex向后移动
  • 尾部继续添加元素时,尾部空位不够将继续添加到头部的空位,当size大于我们设定的长度时,才进行动态扩容
    数据结构和算法(七)--循环队列(CircleQueue)和双端循环队列(CircleDeque)_第1张图片

循环队列接口设计

  1. int size()
    返回此队列的长度
  2. boolean isEmpty()
    判断此队列是否为空
  3. void enQueue(E e)
    入队,需要动态扩容,获取数组最后一个位置把传入参数赋值到数组里面
  4. E deQueue()
    出队,需要动态缩容,获取头部元素返回,frontIndex向后移动,当是数组最后一个位置时,需要移动到头部
  5. E front()
    获取头部元素
  6. clear()
    清空数组并复位size和frontIndex

循环队列代码实现

/**
 * @author maolin yuan
 * @version 1.0
 * @date 2021/6/1 16:02
 */
public class CustomCircleQueue<E> {

    private Object[] elements;

    private int frontIndex;

    private int size;

    public CustomCircleQueue() {
        this.elements = new Object[5];
        this.frontIndex = 0;
        this.size = 0;
    }

    public int size() {
        return size;
    }

    public boolean isEmpty() {
        return size == 0;
    }

    public void enQueue(E e) {
        checkSize();
        dynamicCapability(true);
        elements[(frontIndex + size) % elements.length] = e;
        size++;

    }

    public E deQueue() {
        checkSize();
        dynamicCapability(false);

        E oldElement = front();
        elements[frontIndex] = null;
        frontIndex = (frontIndex + 1) % elements.length;
        size--;
        return oldElement;
    }

    public E front() {
        return (E) elements[frontIndex];
    }

    public void clear() {
        this.elements = new Object[5];
        this.frontIndex = 0;
        this.size = 0;
    }

    private void checkSize() {
        if (size + 1 > elements.length) {
            throw new RuntimeException("circle queue check index exception");
        }
    }

    private void dynamicCapability(boolean flag) {
        if (flag && (size * 1.5 > elements.length)) {
            Object[] newElements = new Object[elements.length * 2];
            dynamicCapability(newElements);
        }

        if (!flag && (size < elements.length / 4)) {
            Object[] newElements = new Object[elements.length / 2];
            dynamicCapability(newElements);
        }
    }

    private void dynamicCapability(Object[] newElements) {
        for (int i = 0; i < size; i++) {
            newElements[i] = elements[(frontIndex + i) % elements.length];
        }
        elements = newElements;
        frontIndex = 0;
    }

    @Override
    public String toString() {
        return "CustomCircleQueue{" +
                "elements=" + Arrays.toString(elements) +
                ", frontIndex=" + frontIndex +
                ", size=" + size +
                '}';
    }

    public static void main(String[] args) {
        CustomCircleQueue<Integer> queue = new CustomCircleQueue<>();
        for (int i = 0; i < 10; i++) {
            queue.enQueue(i);
        }

        for (int i = 0; i < 5; i++) {
            queue.deQueue();
        }

        for (int i = 0; i < 10; i++) {
            queue.enQueue(10 + i);
        }

        System.out.println(queue);
        while (!queue.isEmpty()) {
            System.out.println(queue.deQueue());
        }

    }
}

循环双端队列

  • 和循环队列大同小异,主要是增加两个方法(void enQueueFront(E e) 头部入队,E deQueueRear() 尾部出队)

循环双端队列代码实现

/**
 * @author maolin yuan
 * @version 1.0
 * @date 2021/6/2 10:52
 */
public class CustomCircleDeque<E> {

    private Object [] elements;

    private int size;

    private int frontIndex;

    public CustomCircleDeque(){
        this.elements = new Object[5];
        this.size = 0;
        this.frontIndex = 0;
    }

    public int size(){
        return size;
    }

    public boolean isEmpty(){
        return size == 0;
    }

    public void enQueueRear(E e){
        checkSize();
        dynamicCapability(true);
        elements[obtainTotalIndex(size)] = e;
        size++;
    }

    public E deQueueRear(){
        checkSize();
        dynamicCapability(false);
        E oldElement = rear();
        elements[obtainTotalIndex(size - 1)] = null;
        size--;
        return oldElement;
    }

    public void enQueueFront(E e){
        checkSize();
        dynamicCapability(true);
        frontIndex = obtainTotalIndex(-1);
        elements[frontIndex] = e;
        size ++;
    }

    public E deQueueFront(){
        checkSize();
        dynamicCapability(false);

        E oldElement = front();
        elements[frontIndex] = null;
        frontIndex = obtainTotalIndex(1);
        size--;
        return oldElement;
    }

    public E front(){
        return (E) elements[frontIndex];
    }

    public E rear(){
        return (E) elements[obtainTotalIndex(size - 1)];
    }

    private void checkSize() {
        if (size + 1 > elements.length) {
            throw new RuntimeException("circle queue check index exception");
        }
    }

    private void dynamicCapability(boolean flag) {
        if (flag && (size * 1.5 > elements.length)) {
            Object[] newElements = new Object[elements.length * 2];
            dynamicCapability(newElements);
        }

        if (!flag && (size < elements.length / 4)) {
            Object[] newElements = new Object[elements.length / 2];
            dynamicCapability(newElements);
        }
    }

    private void dynamicCapability(Object[] newElements) {
        for (int i = 0; i < size; i++) {
            newElements[i] = elements[(frontIndex + i) % elements.length];
        }
        elements = newElements;
        frontIndex = 0;
    }

    private int obtainTotalIndex(int index){
        index += frontIndex;
        if (index < 0){
            return index + elements.length;
        }
        return index % elements.length;
    }

    public void clear(){
        this.elements = new Object[5];
        this.size = 0;
        this.frontIndex = 0;
    }

    @Override
    public String toString() {
        return "CustomCircleDeque{" +
                "elements=" + Arrays.toString(elements) +
                ", size=" + size +
                ", frontIndex=" + frontIndex +
                '}';
    }

    public static void main(String[] args) {
        CustomCircleDeque<Integer> deque = new CustomCircleDeque<>();
        for (int i = 0; i < 10; i++) {
            deque.enQueueFront(i);
            deque.enQueueRear(i + 100);
        }
        System.out.println(deque);
        for (int i = 0; i < 4; i++) {
            System.out.println(deque.deQueueFront());
            System.out.println(deque.deQueueRear());
        }
        System.out.println(deque);
        for (int i = 0; i < 2; i++) {
            deque.enQueueFront(i + 10);
        }
        System.out.println(deque);
        while (!deque.isEmpty()){
            System.out.println(deque.deQueueFront());
        }
    }
}

练习

1.用队列实现栈
  • 链接:https://leetcode-cn.com/problems/implement-stack-using-queues/
  • 思路:两个队列保持一个是空,弹栈时,把装有元素的那个队列除了最后一个元素都出队到另一个队列,然后剩下的最后一个元素就是需要弹栈的元素。
  • 代码实现:
class MyStack {

    private final Queue<Integer> queue1 = new LinkedList<>();
    private final Queue<Integer> queue2 = new LinkedList<>();

    /** Initialize your data structure here. */
    public MyStack() {

    }

    /** Push element x onto stack. */
    public void push(int x) {
        if (queue1.isEmpty()){
            queue2.add(x);
        }else if (queue2.isEmpty()){
            queue1.add(x);
        }

    }

    /** Removes the element on top of the stack and returns that element. */
    public int pop() {
        if (queue2.isEmpty()){
            while (queue1.size() > 1){
                queue2.add(queue1.poll());
            }
            Integer poll = queue1.poll();
            assert poll != null;
            return poll;
        }else if (queue1.isEmpty()){
            while (queue2.size() > 1){
                queue1.add(queue2.poll());
            }
            Integer poll = queue2.poll();
            assert poll != null;
            return poll;
        }
        throw new RuntimeException("stack is empty");
    }

    /** Get the top element. */
    public int top() {
        if (queue2.isEmpty()){
            while (queue1.size() > 1){
                queue2.add(queue1.poll());
            }
            Integer poll = queue1.peek();
            queue2.add(queue1.poll());
            assert poll != null;
            return poll;
        }else if (queue1.isEmpty()){
            while (queue2.size() > 1){
                queue1.add(queue2.poll());
            }
            Integer poll = queue2.peek();
            queue1.add(queue2.poll());
            assert poll != null;
            return poll;
        }
        throw new RuntimeException("stack is empty");
    }

    /** Returns whether the stack is empty. */
    public boolean empty() {
        return queue1.isEmpty()&& queue2.isEmpty();
    }

    public static void main(String[] args) {
        MyStack myStack = new MyStack();
        System.out.println(myStack.empty());
        for (int i = 0; i < 10; i++) {
            myStack.push(i);
        }
        for (int i = 0; i < 5; i++) {
            System.out.println(myStack.pop());
        }
        for (int i = 0; i < 5; i++) {
            myStack.push(i + 10);
        }
        while (!myStack.empty()){
            System.out.println(myStack.pop());
        }
    }
}

/**
 * Your MyStack object will be instantiated and called as such:
 * MyStack obj = new MyStack();
 * obj.push(x);
 * int param_2 = obj.pop();
 * int param_3 = obj.top();
 * boolean param_4 = obj.empty();
 */

你可能感兴趣的:(数据结构和算法,队列,数据结构,java)