很多人觉得做一件事付出了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);
// 首部添加
@Override
public 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;
}
// 尾部添加
@Override
public 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;
}
// 头部删除
@Override
public 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;
}
// 尾部删除
@Override
public 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;
}
// 首部获取
@Override
public E peekFirst() {
if (isEmpty()) {
return null;
}
Node<E> node = sentinel.next;
return node.value;
}
// 尾部获取
@Override
public E peekLast() {
if (isEmpty()) {
return null;
}
Node<E> node = sentinel.prev;
return node.value;
}
// 判空
@Override
public boolean isEmpty() {
return size==0;
}
// 判满
@Override
public boolean isFull() {
return size==capacity;
}
// 迭代器
@Override
public Iterator<E> iterator() {
return new Iterator<E>() {
Node<E> p = sentinel.next;
@Override
public boolean hasNext() {
return p!=sentinel;
}
@Override
public 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 {
// 添加节点
@Test
public 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);
}
// 移除节点
@Test
public 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> {
// 两个工具方法 1
static int inc(int i,int length){
if (i + 1 >= length){
return 0;
}
return i + 1;
}
// 两个工具方法 2
static 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];
}
@Override
public boolean offerFirst(E e) {
if (isFull()){
return false;
}
head = dec(head, array.length);
array[head] = e;
return true;
}
@Override
public boolean offerLast(E e) {
if (isFull()){
return false;
}
array[tail] = e;
tail = inc(tail, array.length);
return true;
}
@Override
public E pollFirst() {
if (isEmpty()){
return null;
}
E e = array[head];
// 帮助垃圾回收
array[head] = null;
head = inc(head,array.length);
return e;
}
@Override
public E pollLast() {
if (isEmpty()){
return null;
}
tail = dec(tail,array.length);
E e = array[tail];
// 帮助垃圾回收
array[tail] = null;
return e;
}
@Override
public E peekFirst() {
if (isEmpty()){
return null;
}
return array[head];
}
@Override
public E peekLast() {
if (isEmpty()){
return null;
}
return array[dec(tail, array.length)];
}
@Override
public boolean isEmpty() {
return head == tail;
}
@Override
public boolean isFull() {
if (tail > head){
return tail - head == array.length - 1;
} else if (tail < head) {
return head - tail == 1;
} else {
return false;
}
}
@Override
public Iterator<E> iterator() {
return new Iterator<E>() {
int p = head;
@Override
public boolean hasNext() {
return p != tail;
}
@Override
public 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 {
@Test
public 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);
}
@Test
public 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