(三) 数据结构之队列

队列

队列的特点

  • 队列也是一种线性结构
  • 相比数组, 队列对应的操作是数组的子集
  • 只能从一端(队尾)添加元素, 只能从另一端(队首)取出元素
  • 队列是一种先进先出的数据结构(先到先得)
  • First In First Out (FIFO)

队列接口的实现

Queue

  • void enqueue(E)
  • E dequeue()
  • E getFront()
  • int getSize()
  • boolean isEmpty()

队列的实现

数组队列

使用数组实现一个队列

实现代码

package pers.jssd.queue;

/**
 * 简单的用数组实现队列
 *
 * @author jssd
 * Create 2019-07-24 8:56
 */
public class ArrayQueue<E> implements Queue<E>{

	private Array<E> array;

	public ArrayQueue() {
		array = new Array<>();
	}

	public ArrayQueue(int capacity) {
		array = new Array<>(capacity);
	}

	@Override
	public void enqueue(E e) {
		array.addLast(e);
	}

	@Override
	public E dequeue() {
		return array.removeFirst();
	}

	@Override
	public E getFront() {
		return array.getFirst();
	}

	@Override
	public int getSize() {
		return array.getSize();
	}

	/**
	 * 取得队列的容量
	 * @return 队列的容量
	 */
	public int getCapacity() {
		return array.getCapacity();
	}

	@Override
	public boolean isEmpty() {
		return array.isEmpty();
	}

	@Override
	public String toString() {
		return "ArrayQueue{" +
				"array=" + array +
				'}';
	}
}

测试代码

package pers.jssd.test;

import pers.jssd.queue.ArrayQueue;
import pers.jssd.queue.Queue;

/**
 * @author jssd
 * Create 2019-07-24 8:53
 */
public class Main {
   public static void main(String[] args) {
      Queue<Integer> queue = new ArrayQueue<>();
      for (int i = 0; i < 10; i++) {
         queue.enqueue(i);
         System.out.println("queue = " + queue);

         if (i % 3 == 2) {
            queue.dequeue();
            System.out.println("queue = " + queue);
         }
      }
   }
}

复杂度分析

ArrayQueue

  • void enqueue(E) O(1) 均摊复杂度
  • E dequeue() O(n)
  • E getFront() O(1)
  • int getSize() O(1)
  • boolean isEmpty() O(1)

可以看到, 我们出队的时候, 每次删除数组的第一个元素, 所有元素都要向前移动一个位置, 所以复杂度是O(n)级别的. 那么, 我们能不能对此优化呢, 下面我们实现循环队列

循环队列

程序维护两个指针, 一个指向队首, 一个指向队尾. 入队的时候, 队尾加一, 出队的时候, 队首加一. 当指针加到数组的尾部的时候, 循环到数组的开头. 实现了一个循环的队列

实现代码

package pers.jssd.queue;

import java.util.Arrays;

/**
 * 使用数组实现的循环队列, 当front==tail的时候, 队列为空, 当front == (tail+1) % capacity的时候, 队列已满
 *
 * @author jssd
 * Create 2019-07-24 9:05
 */
public class LoopQueue<E> implements Queue<E> {

   /**
    * 底层用数组实现
    */
   private E[] data;

   /**
    * 指向队首的指针
    */
   private int front;
   /**
    * 指向队尾的指针
    */
   private int tail;

   @SuppressWarnings("unchecked")
   public LoopQueue(int capacity) {
      front = tail = 0;
      data = (E[]) new Object[capacity + 1];
   }

   public LoopQueue() {
      this(10);
   }

   @Override
   public void enqueue(E e) {
      if ((tail + 1) % data.length == front) {
         resize(data.length * 2);
      }
      data[tail] = e;
      tail = (tail + 1) % data.length;
   }

   /**
    * 扩容数组
    *
    * @param newCapacity 扩容新数组的容量
    */
   @SuppressWarnings("unchecked")
   private void resize(int newCapacity) {
      E[] newData = (E[]) new Object[newCapacity];
      for (int i = 0; i < getSize(); i++) {
         newData[i] = data[(i + front) % data.length];
      }
      tail = getSize();
      front = 0;
      data = newData;
   }

   @Override
   public E dequeue() {
      if (front == tail) {
         throw new IllegalArgumentException("error, can't dequeue from empty queue");
      }
      E ret = data[front];
      front = (front + 1) % data.length;
      if (getSize() < data.length / 4) {
         resize(data.length / 2);
      }

      return ret;
   }

   @Override
   public E getFront() {
      return data[front];
   }

   @Override
   public int getSize() {
      return (tail + data.length - front) % data.length;
   }

   /**
    * 取得队列容量
    * @return 队列的容量
    */
   public int getCapacity() {
      return data.length - 1;
   }

   @Override
   public boolean isEmpty() {
      return front == tail;
   }

   @Override
   public String toString() {
      StringBuilder sb = new StringBuilder();
      sb.append("Queue{").append("data=[");
      for (int i = front; i != tail; i = (i + 1) % data.length) {
         sb.append(data[i]);
         if ((i + 1) % data.length != tail) {
            sb.append(", ");
         }
      }
      sb.append("], front=").append(front).append(", tail=").append(tail).append(", size=")
            .append(getSize()).append(", capacity = ").append(getCapacity()).append("}");
      return sb.toString();
   }
}

复杂度分析

此时, dequeue()方法也变成了O(1)级别的复杂度(均摊), 所以整个循环队列是O(1)级别的复杂度

两种队列比较

代码比较两种队列在执行1000000的数据时, 用时差异

package pers.jssd.test;

import pers.jssd.queue.ArrayQueue;
import pers.jssd.queue.LoopQueue;
import pers.jssd.queue.Queue;

/**
 * @author jssd
 * Create 2019-07-24 9:49
 */
public class CompareQueue {
   public static void main(String[] args) {
      int opCount = 1000000;
      ArrayQueue<Integer> arrayQueue = new ArrayQueue<>();
      LoopQueue<Integer> loopQueue = new LoopQueue<>();
      System.out.println("arrayQueue use time = " + test(arrayQueue, opCount));
      System.out.println("loopQueue use time = " + test(loopQueue, opCount));
   }

   /**
    * 测试队列执行一定次数使用的时间
    * @param queue 执行的队列
    * @param opCount 执行的次数
    * @return 返回执行的时间
    */
   @SuppressWarnings("unchecked")
   private static double test(Queue queue, int opCount) {
      long start = System.nanoTime();
      for (int i = 0; i < opCount; i++) {
         queue.enqueue(i);
         if (i % 3 == 2) {
            queue.dequeue();
         }
      }
      long end = System.nanoTime();
      return (end - start) / 1000000000.0;
   }
}

时间差异如下

arrayQueue use time = 26.6430455
loopQueue use time = 0.0463479
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值