赞
踩
双端队列(Double-ended Queue,简称deque)是一种线性数据结构,它允许在两端进行插入和删除操作。这意味着与普通队列(FIFO,先进先出)或栈(LIFO,后进先出)不同,双端队列的使用者不仅可以从队列的一端(通常称为“尾”端)添加元素(enqueue),也可以从这一端删除元素(dequeue)。同时,双端队列还支持在另一端(通常称为“头”端)执行相同的操作,即在队列头部添加(enqueue at head)和删除(dequeue at head)元素。
因此,双端队列的特点包括:
综上所述,双端队列提供了一种更灵活、功能更强大的线性数据结构,能够适应更多样化的数据处理需求。
双端队列的实现方式主要有两种:数组存储和链表存储
在实际应用中,选择何种实现方式取决于具体需求,包括对内存使用、插入/删除效率、随机访问性能等因素的权衡。
package deque; /** * 双端队列 */ public interface Deque<E> { /** * 在队列开头添加元素 * * @param e * @return */ boolean offerFirst(E e); /** * 在队列结尾添加元素 * * @param e * @return */ boolean offerLast(E e); /** * 在队列开头读取元素,并删除该元素 * * @return */ E pollFirst(); /** * 在队列尾部读取元素,并删除该元素 * * @return */ E pollLast(); /** * 在队列开头查询元素,但不删除 * * @return */ E peekFirst(); /** * 在队列尾部查询元素,但不删除 * * @return */ E peekLast(); /** * 判断队列是否为空 * * @return */ boolean isEmpty(); /** * 检查队列是否已满 * * @return */ boolean isFull(); }
package deque; /** * 使用双向循环链表实现双端队列 * * @param <E> */ public class DequeLinkedList<E> implements Deque<E> { /** * 哨兵 */ private Node<E> sentinel = new Node<>(null, null, null); /** * 队列长度 */ private int dequeLength; /** * 队列值的个数 */ private int size; /** * @param dequeLength 设置队列长度 */ public DequeLinkedList(int dequeLength) { this.sentinel.prev = sentinel; this.sentinel.next = sentinel; this.dequeLength = dequeLength; this.size = 0; } /** * 节点类 * * @param <E> */ static class Node<E> { Node<E> prev;//前节点 E value;//节点值 Node<E> next;//后节点 public Node(Node<E> prev, E value, Node<E> next) { this.prev = prev; this.value = value; this.next = next; } } /** * 在队列开头添加元素 * * @param e * @return */ @Override public boolean offerFirst(E e) { //判断队列是否已满 if (isFull()) { return false; } Node<E> a = sentinel; Node<E> b = sentinel.next; Node<E> offered = new Node<>(a, e, b); a.next = offered; b.prev = offered; size++; return true; } /** * 在队列结尾添加元素 * * @param e * @return */ @Override public boolean offerLast(E e) { //判断队列是否已满 if (isFull()) { return false; } Node<E> a = sentinel.prev; Node<E> b = sentinel; Node<E> offered = new Node<>(a, e, b); a.next = offered; b.prev = offered; size++; return true; } /** * 在队列开头读取元素,并删除该元素 * * @return */ @Override public E pollFirst() { //判断队列是否为空 if (isEmpty()) { return null; } Node<E> data = sentinel.next; Node<E> next = data.next; sentinel.next = next; next.prev = sentinel; size--; return data.value; } /** * 在队列尾部读取元素,并删除该元素 * * @return */ @Override public E pollLast() { //判断队列是否为空 if (isEmpty()) { return null; } Node<E> data = sentinel.prev; Node<E> prev = data.prev; prev.next = sentinel; sentinel.prev = prev; size--; return data.value; } /** * 在队列开头查询元素,但不删除 * * @return */ @Override public E peekFirst() { //判断队列是否为空 if (isEmpty()) { return null; } return sentinel.next.value; } /** * 在队列尾部查询元素,但不删除 * * @return */ @Override public E peekLast() { //判断队列是否为空 if (isEmpty()) { return null; } return sentinel.prev.value; } /** * 判断队列是否为空 * * @return */ @Override public boolean isEmpty() { //队列值个数等于=0则队列为空! return size == 0; } /** * 检查队列是否已满 * * @return */ @Override public boolean isFull() { //队列值个数等于队列长度则表示队列已满。 return size == dequeLength; } }
在头部插入元素:
判断队列是否已满。没有则,将head索引后退一步(-1)。让后在该索引位置插入值。
在尾部插入元素:
判断队列是否已满。没有则,直接在tail索引位置后添加新元素,并更新tail索引前进一步(+1)。
从头部获取元素:
先获取head索引位置的元素,再将head索引位置的元素清空(垃圾回收),最后将head索引前移一位(+1)。
从尾部删除元素:
先将tail索引位置后移一位(-1);再获取 tail索引位置的元素,最后将tail索引位置的元素清空(垃圾回收);
package deque; /** * 使用循环数组实现双端队列 * * @param <E> */ public class DequeArray<E> implements Deque<E> { /** * 队列数组 */ private E[] dequeArray; /** * 队列长度 */ private int dequeLength; /** * 头指针 */ private int head; /** * 尾指针 */ private int tail; /** * 队列值个数 */ private int size; /** * @param dequeLength 设置队列长度 */ public DequeArray(int dequeLength) { this.dequeArray = (E[]) new Object[dequeLength]; this.dequeLength = dequeLength; this.head = 0; this.tail = 0; this.size = 0; } /** * 获取增长索引位置 * * @return */ private int increase(int index) { //如果索引位置加1大于数组的下表,那么返回索引0; if (index + 1 > dequeLength - 1) { return 0; } return index + 1; } /** * 获取下降索引位置 * * @return */ private int decline(int index) { if (index - 1 < 0) { return dequeLength - 1; } return index - 1; } /** * 在队列开头添加元素 * * @param e * @return */ @Override public boolean offerFirst(E e) { //检查队列是否已满 if (isFull()) { return false; } head = decline(head);//头部添加队列要先获取索引值。(索引位置先-1) dequeArray[head] = e; //赋值 size++; return true; } /** * 在队列结尾添加元素 * * @param e * @return */ @Override public boolean offerLast(E e) { //检查队列是否已满 if (isFull()) { return false; } dequeArray[tail] = e; //先赋值 tail = increase(tail);//再更新索引 size++; return true; } /** * 在队列开头读取元素,并删除该元素 * * @return */ @Override public E pollFirst() { //判断队列是否为空 if (isEmpty()) { return null; } E e = dequeArray[head]; dequeArray[head] = null;//将取出的值清空。回收垃圾; head = increase(head); size--; return e; } /** * 在队列尾部读取元素,并删除该元素 * * @return */ @Override public E pollLast() { //判断队列是否为空 if (isEmpty()) { return null; } int tail = decline(this.tail); E e = dequeArray[tail]; dequeArray[tail] = null;//将取出的值清空。回收垃圾; size--; return e; } /** * 在队列开头查询元素,但不删除 * * @return */ @Override public E peekFirst() { //判断队列是否为空 if (isEmpty()) { return null; } return dequeArray[head]; } /** * 在队列尾部查询元素,但不删除 * * @return */ @Override public E peekLast() { //判断队列是否为空 if (isEmpty()) { return null; } return dequeArray[decline(tail)]; } /** * 判断队列是否为空 * * @return */ @Override public boolean isEmpty() { //队列值个数等于0 return size == 0; } /** * 检查队列是否已满 * * @return */ @Override public boolean isFull() { //队列值个数等于队列长度; return size == dequeLength; } }
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。