这篇文章我们来看一下数据结构中的队列
目录
1.概述
计算机科学中,queue是以顺序的方式维护的一组数据集合,在一端添加数据,从另一端移除数据。习惯来说,添加的一端称为尾,移除的一端称为头,就如同生活中的排队买商品。
简单来说:队列是顺序存储的线性数据集合,一头插入,一头删除,先进先出
2.队列的接口设计
下面来看一下队列的接口设计:
3.用链表来实现队列
下面看一下用单向环形带哨兵的链表来实现队列:
代码如下:
import java.util.Iterator;
public class L8_LinkedListQueue <E> implements L8_QueueInter<E>,Iterable<E>{
/**
* 定义节点类
* */
public static class Node<E>{
E value;
Node<E> next;
public Node(E value, Node<E> next) {
this.value = value;
this.next = next;
}
}
Node<E> head = new Node<>(null,null);//哨兵结点
Node<E> tail = head;//队列尾指针
int size ;//节点数
int capacity = Integer.MAX_VALUE;//容量
public L8_LinkedListQueue(int capacity) {//带参构造
this.capacity = capacity;
tail.next = head;//链表的尾节点指向头结点,构成环
}
public L8_LinkedListQueue() {
tail.next = head;//链表的尾节点指向头结点,构成环
}
@Override
public boolean offer(E value) {
if (isFull())
return false;
Node<E> added = new Node<>(value,head);//构建新节点,因为是环,所以新节点指向哨兵结点
tail.next = added;//将节点加入到队列中
tail = added;//更改尾指针的指向
size++;
return true;
}
@Override
public E poll() {
if (isEmpty())
return null;
Node<E> first = head.next;//定义指针指向哨兵节点的下一个节点,即第一个节点
head.next = first.next;//断开第一个节点的连接
size--;
return first.value;//返回第一个节点的值
}
@Override
public E peek() {
if (isEmpty())
return null;
return head.next.value;
}
@Override
public boolean isEmpty() {
return head == tail;
}
@Override
public boolean isFull() {
return size==capacity;
}
@Override
public Iterator<E> iterator(){
return new Iterator<E>() {
Node<E> p = head.next;
@Override
public boolean hasNext() {
return p != head;
}
@Override
public E next() {
E value = p.value;
p = p.next;
return value;
}
};
}
public void show(){
Node<E> p = head.next;
while (p != head){
System.out.println(p.value);
p = p.next;
}
}
}
说明:用链表来实现队列其实是很简单的。上面写的是用链表来实现泛型队列。由于链表的可变化性,所以就不存在扩容的相关操作(其实这上面的扩容很简单,就改变一下参数的值就行)
4.用环形数组来实现队列
下面来看一下如何用环形数组来实现队列
这里有两种实现方法,但区别不大,都看一下:
第一种:
第二种:
给出第二种的代码:
public class L9_ArrayQueue2<E> implements L8_QueueInter<E>{
private E[] array;//定义一个泛型数组
private int head = 0;//头指针
private int tail = 0;//尾指针
private int size = 0;//队列中有效元素的个数
public L9_ArrayQueue2(int capacity) {
array = (E[]) new Object[capacity];//创建数组,给数组定容量
}
@Override
public boolean offer(E value) {
if (isFull())//数组满了,就添加不了了
return false;
array[tail] = value;//尾指针处的值等于入队的值
tail = (tail+1)%array.length;//尾指针后移
size++;
return true;
}
@Override
public E poll() {
if (isEmpty())
return null;
E value = array[head];//定义变量,存储元素,
head = (head+1)%array.length;//头指针后移
size--;
return value;
}
@Override
public E peek() {
if (isEmpty())
return null;
return array[head];//就直接返回当前头指针处的元素了
}
@Override
public boolean isEmpty() {
return size==0;//一开始数组为空,如果头尾指针相等了,数组就为空
}
@Override
public boolean isFull() {
return size==array.length;//如果尾指针+1等于头了,就满了
}
}
说明:
- 数组的环,只不过是一种逻辑上的环,具体是靠两个指针来实现的
- 使用环形数组而不是使用普通数组的原因是入队和出队操作更加的方便。普通数组的出对要将后面的元素都往前移一位,而环形数组可以直接移动指针,很方便
- 普通数组的头尾是固定的,而环形数组由于它是环,所以它的任意一处都可以是头和尾。
5.可扩容的泛型队列
下面看一下可扩容的泛型队列:
public class L10_EQueue<E> {
private E[] array = (E[]) new Object[10];
private int p = 0;//头指针
private int size = 0;//有效元素个数
public void offer(E value){
if (isFull()){
E[] brr = (E[])new Object[array.length*2];
for (int i = 0; i < array.length; i++) {
brr[i] = array[i];
}
array = brr;
}
array[size] = value;
size++;
}
public E pull(){
if (isEmpty())
return null;
E value = array[p];
p++;
return value;
}
public Boolean isEmpty(){
return size==0;
}
public Boolean isFull(){
return size==array.length;
}
}
说明:可扩容的泛型队列的一般是用数组实现的,如果用链表来实现,那就不用管扩容问题了。这里是使用了指针和容量两个变量来控制,当然也可以使用双指针来控制。
6.队列来实现二叉数的层序遍历
具体代码:
import java.util.LinkedList;
/*
* 用队列来实现二叉树的层次遍历
* */
public class L11_QueuePractice {
/*树的节点类*/
public static class TreeNode{
public int val;
public TreeNode left;
public TreeNode right;
public TreeNode(int val) {
this.val = val;
}
public TreeNode(TreeNode left, int val, TreeNode right) {
this.val = val;
this.left = left;
this.right = right;
}
@Override
public String toString(){
return String.valueOf(this.val);
}
}
public static void main(String[] args) {
TreeNode root = new TreeNode(
new TreeNode(
new TreeNode(4),
2,
new TreeNode(5)
),
1,
new TreeNode(
new TreeNode(6),
3,
new TreeNode(7)
)
);
LinkedList<TreeNode> list = new LinkedList<>();
//L8_LinkedListQueue<TreeNode> queue = new L8_LinkedListQueue<>();
list.offer(root);
while ( !list.isEmpty() ){
TreeNode n = list.poll();
System.out.println(n);
if (n.left != null)
list.offer(n.left);
if (n.right != null)
list.offer(n.right);
}
}
}
7.总结
总的来说,队列是很简单的一种数据结构。队列的操作包括:入队、出队、判断是否为空、判断是否满了等操作,除此之外还有扩容操作。队列的实现主要有用链表实现和用数组实现。其中用链表来实现不用考虑扩容问题,用数组实现要考虑扩容问题。其中,队列常考的就是可扩容的泛型队列的实现。