循环队列(普通+双端)

こ雲淡風輕ζ 提交于 2020-02-24 20:18:45

一、普通循环队列

1、普通循环队列明细

       循环队列是针对顺序队列中最大化利用内存空间的一种解决方法,可以解决当队列(数组)不可再插入新元素但队列的实际可用空间并未占满的问题。

       相比普通的队列,多了个指向队头的索引,而且也是只能在头部删除元素,尾部插入元素。故删除元素的时候,该索引需要往后走一个位置(取模)。插入元素还是队尾插入(取模)。

Alt

	public class CircleQueue<E> {
	    private int size = 0;
	    private int front;
	    private E[] elements;
	    private final int DEFAULT_CAPACITY = 10;
	
	    public CircleQueue() {
	        elements = (E[]) new Object[DEFAULT_CAPACITY];
	        front = 0;
	    }
	
	    public boolean isEmpty() {
	        return size == 0;
	    }
	
	    public void offer(E element) {
	        ensureCapacity(size + 1);
	        elements[(front + size) % elements.length] = element;   //需要将数组中的索引转换为循环队列中的索引
	        size++;
	    }
	
	    public E remove() {
	        E element = elements[front];
	        elements[front] = null;   //清空内存指针指向的对象空间
	        front = (front + 1) % elements.length;  //需要将数组中的索引转换为循环队列中的索引
	        size--;
	        return element;
	    }
	
	    public E peek() {
	        return elements[front];
	    }
	
	    private void ensureCapacity(int capacity) {
	        int oldCapacity = elements.length;
	        int newCapacity = oldCapacity + (oldCapacity >> 1);  //扩容为1.5倍
	        if (capacity > oldCapacity) {
	            E[] newArray = (E[]) new Object[newCapacity];
	            for (int i = 0; i < size; i++) {
	                newArray[i] = elements[(front + i) % oldCapacity];  //防止数据错乱, 将队列置为头->尾顺序
	            }
	            front = 0;    //队列头重新指向0
	            elements = newArray;
	            System.out.println(oldCapacity + "扩容为" + newCapacity);
	        }
	    }
	
	    @Override
	    public String toString() {
	        StringBuilder str = new StringBuilder();
	        str.append("capacity=").append(elements.length).append(", size=").append(size);
	        str.append(", front=").append(front);
	        if (elements.length != 0) {
	            str.append(", [").append(elements[0]);
	            for (int i = 1; i < elements.length; i++) {
	                str.append(",").append(elements[i]);
	            }
	            str.append("]");
	        }
	        return str.toString();
	    }
	
	    public static void main(String[] args) {
	        CircleQueue<Integer> circleQueue = new CircleQueue<>();
	        //0 1 2 3 4 5 6 7 8 9
	        for (int i = 0; i < 10; i++)
	            circleQueue.offer(i);
	        //null null null null null 5 6 7 8 9
	        for (int i = 0; i < 5; i++)
	            circleQueue.remove();
	        //0 3 6 9 12 5 6 7 8 9
	        for (int i = 0; i < 5; i++)
	            circleQueue.offer(i * 3);
	        //5 6 7 8 9 0 3 6 9 12 15 18 21 null null
	        for (int i = 5; i < 8; i++)
	            circleQueue.offer(i * 3);
	        System.out.println(circleQueue);
	        while (!circleQueue.isEmpty()) {
	            System.out.print(circleQueue.remove() + " ");
	        }
	    }
	
	}

2、测试结果

Alt


二、双向循环队列

1、双向循环队列明细

       双向的循环队列不仅可以在头部删除元素还可以在头部添加元素尾部也可以添加和删除元素,而且也有指向队头的索引。

       循环链表索引需要进行映射,这里对映射函数进行了简单封装。索引映射的时候取模的运算比较慢,可采用加法来优化

Alt

	public class CircleDeque<E> {
	    private int size = 0;
	    private int front;
	    private E[] elements;
	    private final int DEFAULT_CAPACITY = 10;
	
	    public CircleDeque() {
	        elements = (E[]) new Object[DEFAULT_CAPACITY];
	        front = 0;
	    }
	
	    public boolean isEmpty() {
	        return size == 0;
	    }
	
	    public void offerFirst(E element) {
	        ensureCapacity(size + 1);
	//        int index = --front < 0 ? front + elements.length : front;
	        int index = front - 1 < 0 ? front - 1 + elements.length : getIndex(-1);    //封装索引映射
	        elements[index] = element;
	        front = index;
	        size++;
	    }
	
	    public void offerLast(E element) {
	        ensureCapacity(size + 1);
	//        elements[(front + size) % elements.length] = element;   //需要将数组中的索引转换为循环队列中的索引
	        elements[getIndex(size)] = element;   //封装索引映射
	        size++;
	    }
	
	    public E removeFirst() {
	        E element = elements[front];
	        elements[front] = null;   //清空内存指针指向的对象空间
	//        front = (front + 1) % elements.length;  //需要将数组中的索引转换为循环队列中的索引
	        front = getIndex(1);  //封装索引映射
	        size--;
	        return element;
	    }
	
	    public E removeLast() {
	//        int index = (front + size - 1) % elements.length;
	        int index = getIndex(size - 1); //封装索引映射
	        E element = elements[index];
	        elements[index] = null;
	        size--;
	        return element;
	    }
	
	    public E peekFirst() {
	        return elements[front];
	    }
	
	    public E peekLast() {
	        return elements[getIndex(this.size - 1)];
	    }
	
	    /**
	     * 索引映射(映射为循环队列中的索引)
	     *
	     * @param index
	     * @return
	     */
	    private int getIndex(int index) {
	         //优化取模运算
        	return (index + front - elements.length < 0 ? index + front : index + front - elements.length);
//        return (index + this.front) % elements.length;
	    }
	
	    private void ensureCapacity(int capacity) {
	        int oldCapacity = elements.length;
	        int newCapacity = oldCapacity + (oldCapacity >> 1);  //扩容为1.5倍
	        if (capacity > oldCapacity) {
	            E[] newArray = (E[]) new Object[newCapacity];
	            for (int i = 0; i < size; i++) {
	                newArray[i] = elements[(front + i) % oldCapacity];  //防止数据错乱, 将队列置为头->尾顺序
	            }
	            front = 0;    //队列头重新指向0
	            elements = newArray;
	            System.out.println(oldCapacity + "扩容为" + newCapacity);
	        }
	    }
	
	    @Override
	    public String toString() {
	        StringBuilder str = new StringBuilder();
	        str.append("capacity=").append(elements.length).append(", size=").append(size);
	        str.append(", front=").append(front);
	        if (elements.length != 0) {
	            str.append(", [").append(elements[0]);
	            for (int i = 1; i < elements.length; i++) {
	                str.append(",").append(elements[i]);
	            }
	            str.append("]");
	        }
	        return str.toString();
	    }
	
	    public static void main(String[] args) {
	        CircleDeque<Integer> circleDeque = new CircleDeque<>();
	
	        //4 3 2 1 0 100 101 102 103 104 105 106 7 6 5
	        //7 6 5 4 3 2 1 0 100 101 102 103 104 105 106 107 108 109 null null 9 8
	        for (int i = 0; i < 10; i++) {
	            circleDeque.offerFirst(i);
	            circleDeque.offerLast(100 + i);
	        }
	        //7 6 5 4 3 2 1 0 100 101 102 103 104 null null null null null null null 9 8
	        for (int i = 0; i < 5; i++)
	            circleDeque.removeLast();
	
	        System.out.println(circleDeque);
	        while (!circleDeque.isEmpty()) {
	            System.out.print(circleDeque.removeFirst() + " ");
	        }
	    }
	}

2、测试结果

Alt

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!