很多人觉得做一件事付出了10分的努力,却只得到5分的汇报。
其实剩下的五分,是在填补你过往的懒惰。
只有将过往的懒惰填满,
努力才会有正向结果
—— 24.10.3
一、概述
双端队列、队列、栈对比:
队列
一端删除(头),一段添加(尾) 先进先出(FIFO)
栈
一端删除和添加 后进先出(LIFO)
双端队列
队列的两端都可以添加、删除
二、双端队列——双向环形链表实现
双端队列要求队头队尾都可以添加和删除元素
所以为了使所有节点都方便计算,于是添加两个指针prev和next,一个指向每个节点的前一个元素,另一个指向一个每个节点的后一个元素,这样在队列头尾删除或添加元素时,只需要通过节点前后节点的指向,快捷删除
定义哨兵节点sentinel,根据哨兵节点前后指针的指向,进行添加和删除
接口
public interface Deque<E> {/*向队列头部添加元素Returns:bool类型,添加是否成功*/boolean offerFirst(E e);/*向队列尾部添加元素Returns:bool类型,添加是否成功*/boolean offerLast(E e);/*从队列的头部删除元素Returns:返回删除的元素*/E pollFirst();/*从队列的尾部删除元素Returns:返回删除的元素*/E pollLast();/*从队列的头部获取元素Returns:获取头部的元素*/E peekFirst();/*从队列的尾部获取元素Returns:获取尾部的元素*/E peekLast();/*判断队列是否为空Returns:判断是否为空*/boolean isEmpty();/*判断队列是否为满Returns:判断是否为满*/boolean isFull();
}
环形链表类
import java.util.Iterator;/*基于双向环形链表实现双端队列Type parameters <E> —— 队列中元素类型*/
public class LinkedListDeque<E> implements Deque<E>,Iterable<E>{// 容量int capacity;// 元素个数int size;// 哨兵Node<E> sentinel = new Node<>(null, null, null);// 首部添加@Overridepublic boolean offerFirst(E e) {if (isFull()) {return false;}Node<E> a = sentinel;Node<E> b = sentinel.next;// 新添加的节点Node<E> added = new Node<>(a,e,b);a.next = added;b.prev = added;size++;return true;}// 尾部添加@Overridepublic boolean offerLast(E e) {if (isFull()) {return false;}Node<E> a = sentinel.prev;Node<E> b = sentinel;Node<E> added = new Node<>(a,e,b);a.next = added;b.prev = added;size++;return true;}// 头部删除@Overridepublic E pollFirst() {if (isEmpty()){return null;}Node<E> a = sentinel;Node<E> remove = sentinel.next;Node<E> b = sentinel.next.next;a.next = b;b.prev = a;size--;return remove.value;}// 尾部删除@Overridepublic E pollLast() {if(isEmpty()){return null;}Node<E> a = sentinel;Node<E> remove = sentinel.prev;Node<E> b = sentinel.prev.prev;b.next = a;a.prev = b;size--;return remove.value;}// 首部获取@Overridepublic E peekFirst() {if (isEmpty()) {return null;}Node<E> node = sentinel.next;return node.value;}// 尾部获取@Overridepublic E peekLast() {if (isEmpty()) {return null;}Node<E> node = sentinel.prev;return node.value;}// 判空@Overridepublic boolean isEmpty() {return size==0;}// 判满@Overridepublic boolean isFull() {return size==capacity;}// 迭代器@Overridepublic Iterator<E> iterator() {return new Iterator<E>() {Node<E> p = sentinel.next;@Overridepublic boolean hasNext() {return p!=sentinel;}@Overridepublic E next() {E value = p.value;p = p.next;return value;}};}// 节点类 prev指针指向上一个节点,next指针指向下一个节点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;}}public LinkedListDeque(int capacity) {this.capacity = capacity;sentinel.next = sentinel;sentinel.prev = sentinel;}
}
测试类
import static org.junit.jupiter.api.Assertions.*;
import org.junit.Test;import java.util.List;public class TestLinkedListDeque {// 添加节点@Testpublic void offer(){LinkedListDeque<Integer> deque = new LinkedListDeque<>(5);deque.offerFirst(3);deque.offerLast(4);deque.offerFirst(2);deque.offerLast(5);deque.offerFirst(1);assertFalse(deque.offerLast(6));assertIterableEquals(List.of(1,2,3,4,5),deque);}// 移除节点@Testpublic void poll(){LinkedListDeque<Integer> deque = new LinkedListDeque<>(5);deque.offerFirst(3);deque.offerLast(4);deque.offerFirst(2);deque.offerLast(5);deque.offerFirst(1);assertEquals(1, deque.pollFirst());assertEquals(2, deque.pollFirst());assertEquals(3, deque.pollFirst());assertEquals(4, deque.pollFirst());assertEquals(5, deque.pollLast());assertNull(deque.pollLast());assertTrue(deque.isEmpty());}}
三、双端队列——数组实现
基于循环数组实现
head:头指针 tail:尾指针
特点:tail停下来的位置不存储,会浪费一个位置
Type parameters:<E>——队列中元素类型
接口
public interface Deque<E> {/*向队列头部添加元素Returns:bool类型,添加是否成功*/boolean offerFirst(E e);/*向队列尾部添加元素Returns:bool类型,添加是否成功*/boolean offerLast(E e);/*从队列的头部删除元素Returns:返回删除的元素*/E pollFirst();/*从队列的尾部删除元素Returns:返回删除的元素*/E pollLast();/*从队列的头部获取元素Returns:获取头部的元素*/E peekFirst();/*从队列的尾部获取元素Returns:获取尾部的元素*/E peekLast();/*判断队列是否为空Returns:判断是否为空*/boolean isEmpty();/*判断队列是否为满Returns:判断是否为满*/boolean isFull();
}
循环数组类
import java.util.Iterator;public class ArrayDeque<E> implements Deque<E>,Iterable<E> {// 两个工具方法 1static int inc(int i,int length){if (i + 1 >= length){return 0;}return i + 1;}// 两个工具方法 2static int dec(int i,int length){if (i - 1 < 0){return length - 1;}return i - 1;}E[] array;int head;int tail;@SuppressWarnings("all")public ArrayDeque(int capacity){array = (E[]) new Object[capacity+1];}@Overridepublic boolean offerFirst(E e) {if (isFull()){return false;}head = dec(head, array.length);array[head] = e;return true;}@Overridepublic boolean offerLast(E e) {if (isFull()){return false;}array[tail] = e;tail = inc(tail, array.length);return true;}@Overridepublic E pollFirst() {if (isEmpty()){return null;}E e = array[head];// 帮助垃圾回收array[head] = null;head = inc(head,array.length);return e;}@Overridepublic E pollLast() {if (isEmpty()){return null;}tail = dec(tail,array.length);E e = array[tail];// 帮助垃圾回收array[tail] = null;return e;}@Overridepublic E peekFirst() {if (isEmpty()){return null;}return array[head];}@Overridepublic E peekLast() {if (isEmpty()){return null;}return array[dec(tail, array.length)];}@Overridepublic boolean isEmpty() {return head == tail;}@Overridepublic boolean isFull() {if (tail > head){return tail - head == array.length - 1;} else if (tail < head) {return head - tail == 1;} else {return false;}}@Overridepublic Iterator<E> iterator() {return new Iterator<E>() {int p = head;@Overridepublic boolean hasNext() {return p != tail;}@Overridepublic E next() {E e = array[p];p = inc(p,array.length);return e;}};}
}
测试类
import org.junit.Test;import java.util.List;import static org.junit.jupiter.api.Assertions.*;public class TestArrayDeque {@Testpublic void offer() {ArrayDeque<Integer> deque = new ArrayDeque<>(5);deque.offerFirst(1);deque.offerLast(2);deque.offerLast(3);deque.offerLast(4);deque.offerLast(5);assertFalse(deque.offerLast(3));assertIterableEquals(List.of(1,2,3,4,5), deque);}@Testpublic void poll() {ArrayDeque<Integer> deque = new ArrayDeque<>(5);assertTrue(deque.isEmpty());deque.offerFirst(1);deque.offerLast(2);deque.offerLast(3);deque.offerLast(4);deque.offerFirst(5);assertIterableEquals(List.of(5,1,2,3,4),deque);assertTrue(deque.isFull());assertEquals(5, deque.pollFirst());assertEquals(4, deque.pollLast());assertEquals(1 , deque.pollFirst());assertEquals(3 , deque.pollLast());assertEquals(2 , deque.pollFirst());assertNull(deque.pollLast());assertTrue(deque.isEmpty());}
}
垃圾回收机制
因为用泛型实现,所以都是引用类型,我们需要将索引位置的数组值在处理完后赋值为null