队列的实现

一、队列

我们会发现队列和栈很相似,栈是先进后出,队列是先进先出。既然栈可以用双向链表实现,那么队列也应该可以。队列的操作主要有入队和出队,入队就是向队尾添加元素,出队就是向队头删除元素。

队列的实现:

package 队列;

import 链表.LinkedList;

public class Queue<E> {


    private LinkedList<E> list = new LinkedList();


    /**
     * 元素的数量
     * @return
     */
    public int size(){
        return  list.size();
    }

    /**
     * 是否为空
     * @return
     */
    public boolean isEmpty(){
        return list.isEmpty();
    }

    /**
     * 清空元素
     */
    public void clear(){
        list.clear();
    }
    /**
     *  入队
     */
    public void enQueue(E element){
        list.add(element);
    }

    /**
     * 出队
     * @return
     */
    public E deQueue(){
        return list.remove(0);
    }

    /**
     * 获取队列的头元素
     * @return
     */
    public E front(){
        return list.get(0);
    }
}

二、使用两个栈实现队列。

程序就是对数据的操作,而数据结构是数据存储的一种格式。既然栈和队列存储的格式是不一样的,我们要做的就是同样一份数据经过怎样的操作方式 最终在这两种不同的数据结构下,得到相同的结果。 我们写的代码就是数据的操作方式。我写的不是代码,而是对数据的操作方式,嘿嘿。

package 队列;

import 栈.Stack;


public class CQueue {

    Stack<Integer> stack1 = new Stack<>();
    Stack<Integer> stack2 = new Stack<>();
    //队列: push(1) push(2) push(3) pop() push(4) pop()
    //结果: 还在队列的元素 4   3      出队的元素:  2  1

    // 思路:push的元素,我们都放到栈1中。
    // 然后我们需要把1取出,我们只能把栈1的元素取出来放到栈2中
    // 现在又放入4,那我们只能把栈2的元素取出放回栈1,因为4要在3的后面
    // 现在需要取出2,同样将栈1的元素取出放到栈2
    // 栈1:1  2  3    栈2:空
    // 栈1:空         栈2: 3  2       出栈:1
    // 栈1: 2  3  4    栈2:空
    // 栈1: 空         栈2 : 4  3      出栈:2
    // 总结:当栈2不为空时,需要先将栈2的元素取出放入栈1 ,然后push
    //       当栈1不为空时,需要将栈1的元素放入栈2,然后pop
    public void push(int value) {
        while(!stack2.isEmpty()){
            stack1.push(stack2.pop());
        }
        stack1.push(value);
    }

    public int pop() {
        while(!stack1.isEmpty()){
            stack2.push(stack1.pop());
        }
        return stack2.pop();
    }
}

测试结果:

三、双端队列Deque

这个跟上面是一样的,就是多了2个方法而已,直接上代码吧!

package 队列;

import 链表.LinkedList;

public class Deque<E> {
    private LinkedList<E> list = new LinkedList<>();

    public int size() {
        return list.size();
    }

    public boolean isEmpty() {
        return list.isEmpty();
    }

    public void clear() {
        list.clear();
    }

    public void enQueueRear(E element) {
        list.add(element);
    }

    public E deQueueFront() {
        return list.remove(0);
    }

    public void enQueueFront(E element) {
        list.add(0, element);
    }

    public E deQueueRear() {
        return list.remove(list.size() - 1);
    }

    public E front() {
        return list.get(0);
    }

    public E rear() {
        return list.get(list.size() - 1);
    }
}

四、循环队列CircleQueue

循环队列底层可以使用数组实现,首先需要回答的是什么是循环队列。

现在的队列是:11,22,33,44,55。假设现在66,77入队,11,22出队,那么队列就变成了33,44,55,66,77,队头就变成了33。假设现在又有两个数88,99入队,那么队列就变成了33,44,55,66,77,88,99。而88放在了索引为0的位置,99放在了索引为1的位置。这就是循环队列,假设现在又有元素入队,那么此时队列已经满了,和动态数组一样,循环队列也需要动态扩容。

这里有几个方法需要详解,我们一个一个的看。

enQueue(E element):假设我们先不考虑扩容,入队就是向队尾添加元素,那么size就要+1,第一种情况就是上图,front=0,size=5所以我们往队尾添加元素就是elements[front+size] = element;第二种情况就是下图我们往队尾添加一个元素88,front=2,size = 5 ,那elements[7]不就数组越界了吗。所以综合2种情况就是elements[(front+size) % elements.length] = element;

deQueue():出队就是删除队头的元素,那么size就要-1。同样有两种情况,假设front=2,删除队头的元素那么front就变成了3;当front=6时,删除队头的元素后front就变成了0。所以front = (front+1) % elements.length;

ensureCapacity(int capacity):首先来看看我们之前arrayList的扩容方法是怎么写的。扩容的思想就是当数组容量不够时,创建一个新数组,新数组的容量是原来的n倍,然后将旧数组的值取出放到新数组中就行了。那循环数组的扩容和arrayList的扩容有什么区别呢?举个例子就明白了,假设现在的循环队列的容量是7,里面的元素是88,99,33,44,55,66,77   此时循环队列已经装满了,那么循环队列需要扩容。假设队头的元素是33,扩容后数组里存放元素就变成了:33,44,55,66,77,88,99。不就是循环队列是先从队头的元素开始遍历嘛,所以newElements[i] = elements[(i+front)%elements.length]; 

i=0,front=2,length=7;所以newElements[0] = elements[2];
i=1,front=2,length=7;所以newElements[1] = elements[3];

.....依次类推。

最后front = 0;

/**
     * 保证要有capacity的容量
     * @param capacity
     */
    private void ensureCapacity(int capacity) {
        int oldCapacity = elements.length; //原数组的容量
        if(oldCapacity>capacity){
            return;
        }
        //扩容为原来的1.5倍
        int newCapacity = oldCapacity + (oldCapacity >> 1);
        E[] newElements = (E[])new Object[newCapacity];
        for(int i=0;i<size;i++){
            newElements[i] = elements[i];
        }
        elements = newElements;
    }

 

package 队列;

public class CircleQueue<E> {

    private int front;
    private int size;
    private E[] elements;


    public CircleQueue() {
        elements = (E[]) new Object[10];
    }

    public int size() {
       return size;
    }

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

    public void clear() {
        for (int i = 0; i < size; i++) {
			elements[index(i)] = null;
		}
		front = 0;
		size = 0;
    }

    /**
     * 入队:向队尾添加元素
     * @param element
     */
    public void enQueue(E element) {
        ensureCapacity(size + 1);
        elements[(front+size) % elements.length] = element;
        size++;
    }


    /**
     * 出队,删除队头的元素
     * @return
     */
    public E deQueue() {
        E element = elements[front];
        elements[front] = null;
        front = (front+1) % elements.length; //下面的那个例子,假设front=6,现在front+1,因为是循环队列所以front变为了0
        size--;
        return element;
    }

    /**
     * 取出队头的元素
     * @return
     */
    public E front() {
        return elements[front];
    }

    /**
     * 保证要有capacity的容量
     * @param capacity
     */
    private void ensureCapacity(int capacity) {
        int oldCapacity = elements.length;
        if (oldCapacity >= capacity) return;

        // 新容量为旧容量的1.5倍
        int newCapacity = oldCapacity + (oldCapacity >> 1);
        E[] newElements = (E[]) new Object[newCapacity];
        for (int i = 0; i < size; i++) {
            newElements[i] = elements[(i+front)%elements.length];
        }
        elements = newElements;

        // 重置front
        front = 0;
    }
}

 

 

 

 

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值